Linux C 多进程编程(面试考点)
嵌入式开发为什么要移植操作系统?
1.减小软硬件的耦合度,提高软件的移植性
2. 操作系统提供很多库和工具(QT Open CV),提高开发效率
3.操作系统提供多任务机制,______________________? (提高CPU的效率)
4.操作系统提供了丰富的网络协议栈,实现远程传输
Linux C 多进程编程(多进程、多线程)
1.什么是多任务
单任务————多任务
并发————并行
单核CPU ————多核CPU
2.多任务操作的实现方式
进程和线程
程序和进程的区别
程序:
是一组指令和数据的集合,是静态的、存储在磁盘或其他存储介质上的文件。
程序可以被看作是一段代码的集合,描述了在计算机上执行的任务。
程序本身是静态的,需要加载到内存中才能执行。
进程:
是程序的抽象
是动态的
每个进程都都有独立的运行空间(虚拟地址空间),
每个进程都是一个独立的运行单位,拥有各自的权力和责任;(互不干扰)
进程是 安全的任务机制
缺点:开销大(进程创建和进程切换)
进程PID编号
父进程创建子进程
获取进程PID
获取父进程PID
ps命令、top命令和htop命令
进程调度
在许多个已经处于就绪态的进程中,选择决定哪个进程进行调度(基于进程三态)
进程状态:
就绪态、执行态和等待态(阻塞态)
操作系统的核心就是任务(进程)管理
主要分为两大类:抢占式(设置优先级)和非抢占式(不设置优先级)
有如下策略
1.先到先服务; 2.短进程优先; 3.时间片轮转(使用最多); 4.高优先级优先
实时操作系统是一种响应速度快,准确性高(抢占式)
不同任务之间通过双向链表链接
进程分类:
处理器消耗型
渴望获取更多的CPU时间,并消耗掉调度器分配的全部时间片·常见例子:无限死循环、科学计算、影视特效渲染
I/O消耗型
由于等待某种资源通常处于阻塞状态,不需要较长的时间片 常见例子:等待用户输入、GUI程序、文件读写I/O程序
进程同步
多个进程访问同一个文件时;需要互斥访问,否则易产生错误;
操作系统把一次只允许一个进程访问的资源成为临界资源,需要互斥访问
进程的创建
1.fork函数
作业:利用多进程实现,分别从键盘和鼠标读数据:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, char **argv)
{int count = 0;pid_t pid = fork();if (pid < 0) // 错误处理{perror("pid error");exit(-1);}if (pid > 0) // 父进程执行鼠标读操作{int fd1 = open("/dev/input/mouse0", O_RDWR);if (fd1 == -1){perror("fd1 error");exit(-1);}int location = 0;while (1){int r_num1 = read(fd1, &location, sizeof(int));if (r_num1 > 0){printf("mouse loaction=%d\n", location);}}}if (pid == 0) // 子进程执行键盘读写操作{char buffer[1024];memset(buffer, 0, sizeof(buffer));while (1){int r_num = (read(0, buffer, sizeof(buffer) - 1));if (r_num > 0){buffer[r_num] = '\0';printf("%s\n", buffer);}memset(buffer, 0, sizeof(buffer));}}return 0;
}
通过多进程实现父子进程对同一个文件进行写操作
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, char **argv)
{int count = 0;int fd = open("a.txt", O_RDWR | O_APPEND | O_CREAT, 0777);if (fd == -1){perror("fd error");exit(-1);}pid_t pid = fork();if (pid < 0) // 错误处理{perror("pid error");exit(-1);}if (pid > 0) // 父进程操作{write(fd, "hello", 5);write(fd, "world", 5);write(fd, "\n", 1);}if (pid == 0) // 子进程执行操作{write(fd, "FFFFF", 5);write(fd, "KKKKK", 5);write(fd, "\n", 1);}return 0;
}
父子进程的运行顺序,暂时是不需要明白;内部有进程调度算法
2.exec函数族
使用execl函数时,原函数在execl函数后的代码段会不起作用
表头文件:#include <unistd.h>
1. int execl c const char *path,const char *arg,...)
函数说明:
execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中
2. int execv ( const char *path,char *const argv[]);
函数说明:
execv ()用来执行参数path字符串所代表的文件路径,与execl ()不同的地方在于execve ()只需两个参数,第二个参数系利用指针数组来传递给执行文件。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
3. int execlp (const char*file,const char *arg,...);
函数说明: execlp ( )会从 PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当作该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值﹔如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
4.int execvp ( const char *file,char *const argv[]);
函数说明: execvp ( )会从 PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
返回值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
错误代码:请参考execve ()
5.int execve ( const char *filename,char *const argv [].char *const envp[]);
函数说明:execve ()用来执行参数filename字符串所代表的文件路径,第二个参数系利用指针数组来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
返国值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno
中。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, char **argv)
{// 1.参数表直接写入, 以NULL结尾execl("./write1", "./write1", "hello", "world", NULL);// 2.参数表用指针数组的形式, 以NULL结尾char *arg1[] = {"./write2", "welcome", "zhaodeming", NULL};xecv("./write2", arg1);// 3.execlp("/home/zdm/241/PROCESS_CODE/write1", "./write1", "hello", "world", NULL);//execlp("./write1", "./write1", "hello", "world", NULL); // 也可以// 4.char *arg2[] = {"./write2", "welcome", "zhaodmeing", NULL};execvp("/home/zdm/241/PROCESS_CODE/write2", arg2);// 5. e---环境变量char *env[] = {"USR=admin", "PASSWD=12345"};execve("/home/zdm/241/PROCESS_CODE/write2", arg2, env);printf("exce demo ok\n");
}
每个调用exec函数,会覆盖掉后面的代码
常常与fork函数联用:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, char **argv)
{int count = 0;pid_t pid = fork();if (pid < 0) // 错误处理{perror("pid error");exit(-1);}if (pid > 0) // 父进程执行操作{execl("./write1", "./write1", "hello", "world", NULL);}if (pid == 0) // 子进程执行操作{char *arg1[] = {"./write2", "welcome", "zhaodeming", NULL};execvp("/home/zdm/241/PROCESS_CODE/write2", arg1);}return 0;
}
3.vfork 系统调用
对fork的改进对fork的改进更为彻底、简单粗暴
vfork是为子进程立即执行exec的程序而专设计的
无需为子进程复制虚拟内存页或页表,子进程直接共享父进程的资源,直到其成功执行exec或是调用exit退出
在子进程调用exec之前,将暂停执行父进程
子进程中无exec时,则先执行子进程,后执行父进程;
子进程有exec函数时,exec函数前的代码段先执行;执行到exec函数时候,父子进程调用顺序则又不确定(和fork一样)
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, char **argv)
{int count = 0;pid_t pid = vfork();if (pid < 0){perror("pid error");exit(-1);}if (pid > 0){printf("%d\n",count);}if (pid == 0){//count++; //尽量避免在子进程中修改全局变量,容易引发段错误;//exit(-1); execl("./write1","./write1",NULL);}return 0;
}
4.system(const char*command)——库函数
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>int main(int aargc, char **argv)
{system("./write1 system_using read");sleep(3);system("clear");sleep(3);system("ls -l");sleep(3);system("clear");sleep(3);return 0;
}
进程的退出
_exit——系统调用
更多用于异常退出;不会刷新stdio缓冲区;
exit——库函数
内部封装了_exit;会刷新stdio缓冲区;atexit/on_exit注册了退出管理程序,则应使用exit
正常退出: main 调用return
异常退出: 1.任意地方调用exit/_exit; 2.被信号杀死; 3. 调用abort函数
abort函数
以异常方式结束进程:abort ( )将引起进程异常的终止,此时所有已打开的文件流会自动关闭,所有的缓冲区数据也会自动写回。
进程等待
回收进程资源
进程运行终止后,不管进程是正常终止还是异常终止的,必须回收进程所占用的资源。如何查看进程资源?
————————ps命令
为什么要回收进程的资源?
————————不回收资源会导致系统性能下降
父进程运行结束时,会负责回收子进程资源
./a.out进程的父进程是谁?
0,1,2三个进程:OS启动后抑制默默运行,直到关机OS结束运行;
pid=0的进程,称作调度进程;
pid=1的进程, 1.init进程,跟前端用户做交互;2.托管孤儿进程;3.原始父进程(位于/sbin/init目录下,可以restart*stop);
pid=2的进程;页精灵进程
僵尸进程和孤儿进程
僵尸进程:子进程终止后,父进程还在运行,那么在父进程没有回收子进程资源前,此时的子进程就是僵尸进程
孤儿进程:子进程还未结束,父进程先结束,子进程的资源无法回收,此时子进程就是孤儿进程
为了能够回收孤进程终止后的资源,孤儿进程会被托管给我们前面介绍的pid==1的init进程,每当被托管的子进程终止时,init会立即主动回收孤儿进程资源,回收资源的速度很快,所以孤儿进程没有变成僵尸进程的机会。
wait函数
只能父进程等待子进程
函数原型 pid_t wait(int status)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{pid_t pid = fork();if (pid == 0){for (int i = 0; i < 3; ++i){printf("children aaa\n");sleep(1);}exit(3);}if (pid > 0){printf("parents is ok\n");// 1.获取子进程退出状态int ret;wait(&ret);int num = WEXITSTATUS(ret);printf("%d\n", ret);// 2.wait(NULL); // 阻塞,直到子进程结束,再执行下面代码}return 0;
}
waitpid函数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>//父进程等子进程,子进程等子子进程
void die(const char *msg)
{perror(msg);exit(1);
}
void child2_do()
{printf("In child2: execute 'date'\n");sleep(5);if (execlp("date", "date", NULL) < 0){perror("child2 execlp");}
}
void child1_do(pid_t child2, char *argv)
{pid_t pw;do{if (*argv == '1'){pw = waitpid(child2, NULL, 0); // 一直等}else{pw = waitpid(child2, NULL, WNOHANG); // 立刻返回}if (pw == 0){printf("In child1 process:\nThe child2 process has not exited\n");sleep(1);}} while (pw == 0);if (pw == child2){printf("Get child2 %d.\n", pw);sleep(5);if (execlp("pwd", "pwd", NULL) < 0){perror("child1 execlp");}}else{printf("error occured!\n");}
}
void father_do(pid_t child1, char *argv)
{pid_t pw;do{if (*argv == '1'){pw = waitpid(child1, NULL, 0); // 一直等待}else{pw = waitpid(child1, NULL, WNOHANG); // 立刻返回}if (pw == 0){printf("In father process: \nThe child1 process has not exited.\n");sleep(1);}} while (pw == 0);if (pw == child1){printf("Get child1 %d.\n", pw);if (execlp("ls", "ls", "-l", NULL) < 0){perror("father execlp");}}else{printf("error occured ! \n");}
}
int main(int argc, char **argv)
{pid_t child1, child2;if (argc < 3){printf("Usage: waitpid [0 1] [0 1]\n");exit(1);}child1 = fork();if (child1 < 0){die("child1 fork");}else if (child1 == 0){child2 = fork();if (child2 < 0){die("child2 fork");}else if (child2 == 0){child2_do();}else{child1_do(child2, argv[1]);}}else{father_do(child1, argv[2]);}return 0;
}
当父进程没有调用waitpid或wait函数来回收子进程的终止状态时,子进程的终止状态信息将一直保留在系统的进程表中,并使子进程成为僵尸进程。僵尸进程不占用系统资源,但是如果产生大量的僵尸进程却没有及时回收,可能会导致系统资源被占用完。
通过在父进程中调用waitpid或wait函数,父进程会等待子进程的终止并回收其终止状态信息,从而防止子进程变成僵尸进程,并及时释放子进程占用的资源。这样可以保持系统的正常运行和资源的有效利用。
相关文章:

Linux C 多进程编程(面试考点)
嵌入式开发为什么要移植操作系统? 1.减小软硬件的耦合度,提高软件的移植性 2. 操作系统提供很多库和工具(QT Open CV),提高开发效率 3.操作系统提供多任务机制,______________________? (提高C…...

c++一级
与7无关的数 #include<iostream> #include<iomanip> using namespace std; int main() { int n,a,sum0,c0; cin>>n; for(int i1;i<n;i){ if(i%7!0){ ai; c0; …...

Code Lab - 34
GAT里面有一些地方看的不是太懂(GAT里Multi Attention的具体做法),暂时找了参考代码,留一个疑问 1. 一个通用的GNN Stack import torch_geometric import torch import torch_scatter import torch.nn as nn import torch.nn.fun…...

后端返回文件流,前端怎么导出、下载(8种方法可实现)
在前端导出和下载后端返回的文件流时,可以使用以下几种方法: 使用window.open()方法: 在前端使用window.open()方法打开一个新的窗口或标签页,并将后端返回的文件流作为URL传递给该方法。浏览器会自动下载该文件。例如:…...

什么是 ThreadLocal?
ThreadLocal 是 Java 中的一个类,用于在多线程环境下,为每个线程提供独立的变量副本。每个线程可以通过 ThreadLocal 存储和获取数据,而不会影响其他线程的数据。这在某些情况下非常有用,特别是当多个线程需要访问共享数据,但又希望保持数据的隔离性时。 ThreadLocal 主要…...

CANOCO5.0实现冗余分析(RDA)最详细步骤
在地理及生态领域会常使用RDA分析,RDA的实现路径也有很多,今天介绍一下CANOCO软件的实现方法。 1.软件安装 时间调整到2010年 2.数据处理 得有不同的物种或者样点数值,再加上环境因子数据。 3.软件运行 4.结果解读 结果解读主要把握这几点…...

【tkinter 专栏】掷骰子游戏
文章目录 前言本章内容导图1. 需求分析2. 系统功能结构3. 设计流程4. 系统开发环境5. 系统预览6. 窗口布局7. 功能实现用户和电脑选择骰子的点数大小摇骰子过程实现判断游戏结果单击开始按钮进行游戏源代码汇总前言 本专栏将参考《Python GUI 设计 tkinter 从入门到实践》书籍…...

19 NAT穿透|python高级
文章目录 网络通信过程NAT穿透 python高级GIL锁深拷贝与浅拷贝私有化import导入模块工厂模式多继承以及 MRO 顺序烧脑题property属性property装饰器property类属性 魔法属性\_\_doc\_\_\_\_module\_\_ 和 \_\_class\_\_\_\_init\_\_\_\_del\_\_\_\_call\_\_\_\_dict\_\_\_\_str…...

2023常见前端面试题
以下是一些2023年秋招常见的前端面试题及其答案: 1. 请解释一下什么是前端开发? 前端开发是指使用HTML、CSS和JavaScript等技术来构建网页和用户界面的过程。前端开发人员负责将设计师提供的视觉设计转化为可交互的网页,并确保网页在不同设备…...

登录校验-JWT令牌-生成和校验
目录 JWT-生成 具体代码 运行结果如下 JWT-校验 具体代码 运行结果如下 小结 JWT-生成 具体代码 /*** 测试JWT令牌的生成*/Testpublic void TestJWT() {// 设置自定义内容Map<String, Object> claims new HashMap<>();claims.put("id", 1);claims…...

GIT 常用指令
基础指令 $ git init #初始化仓库,在该文件夹创建的为workspace$ git add . #已暂存 [.通配符,全部添加]$ git commit -m "log add file" #提交到仓库,并写了日志 ”log add file“$ git status #查看状态,可查看被修改的文件…...

多目标优化
https://zhuanlan.zhihu.com/p/158705342 概念 单目标优化只有一个优化目标,所以可以比较其好坏。 但是多目标优化,在需要优化多个目标时,容易存在目标之间的冲突,一个目标的优化是以其他目标劣化为代价的,所以我们要…...

odoo的优势
plus,主要是为了能尽早通过开发者审核,加入到chatgpt4 api的开发中去,接入到我们odoo aiCenter中。4的回答,明显比3.5的更聪明了。 可能是由于国内的特殊情况吧,我们的chatgpt模块很受欢迎,我也被问了不少…...

Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【三】
😀前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【三】的分享,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我…...

Kali 软件管理
kali 更新 1. 查看发行版本 ┌──(root㉿kali)-[~] └─# lsb_release -a No LSB modules are available. Distributor ID: Kali Description: Kali GNU/Linux Rolling Release: 2023.2 Codename: kali-rolling2. 查看内核版本 ┌──(root㉿kali)-[~] └─…...

加油站【贪心算法】
加油站 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和…...

java八股文面试[多线程]——死锁、活锁、饥饿
DCL双重锁:TODO 如何预防死锁: 如何查看线程死锁: 知识来源: 【2023年面试】描述一下线程安全活跃态问题,以及竞态条件_哔哩哔哩_bilibili 【2023年面试】如何预防死锁_哔哩哔哩_bilibili 【并发与线程】阿里一面&…...

设计模式——装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 装饰器模式通过将对象包装在装饰器类中,以便动态…...

①matlab的命令掌握
目录 输入命令 命名变量 保存和加载变量 使用内置的函数和常量 输入命令 1.您可以通过在命令行窗口中 MATLAB 提示符 (>>) 后输入命令 任务 使用命令 3*5 将数值 3 和 5 相乘。 答案 3*5 2.除非另有指定,否则 MATLAB 会将计算结果存储在一个名为 ans…...

MySQL----索引
一、索引的概念 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于c语言的链表通过指针指向数据记录的内存地址)。使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该…...

秒杀系统的业务流程以及优化方案(实现异步秒杀)
先看基本的业务流程 那么我们可以看到整个流程都是一个线程来完成的,这样的话耗时还是很长的,那么可不可以采用多线程去实现呢? 首先我们要思考怎么对业务进行拆分,可以想象一个我们去饭店点餐,会有前台接待ÿ…...

Java实现根据商品ID获取1688商品详情跨境属性数据,1688商品重量数据接口,1688API接口封装方法
要通过1688的API获取商品详情跨境属性数据,您可以使用1688开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例,展示如何通过1688开放平台API获取商品详情属性数据接口: 首先,确保您已注册成为1688开放平台的开发者…...

前端面试的性能优化部分(14)每天10个小知识点
目录 系列文章目录前端面试的性能优化部分(1)每天10个小知识点前端面试的性能优化部分(2)每天10个小知识点前端面试的性能优化部分(3)每天10个小知识点前端面试的性能优化部分(4)每天…...

Uniapp笔记(六)uniapp基础
一、腾讯地图 1、uniapp地图渲染 <template><view><map class"map" :longitude"longitude" :latitude"latitude"></map></view> </template> <script>export default {data() {return {longitude:1…...

C++ sort函数用法
sort函数是C标准库中的一个排序算法,头文件是algorithm,用于对容器中的元素进行排序。它可以对任何可排序的容器(如数组、向量、列表等)进行排序。 有以下四个基本用法: 1. 自定义排序规则:可以通过提供自…...

电子仓库预测水浸事件,他怎么做到的?
仓库环境中水浸事件可能导致严重的损失,不仅对货物造成损害,还可能影响设备的正常运行甚至威胁安全。 因此,为了应对这一挑战,引入一套完善的仓库水浸监控系统成为了不可或缺的措施。 客户案例 广东某电子公司是一家领先的电子设…...

CMake调用第三方库的两种方法
为了让连接器搜索到库路径,一般有两种方法 link_directories命令 使用步骤## 在add_executable或add_library前引入第三方库 # 1.引入第三方库,${THIRD_PARTY_PREFIX}为用户定义的第三方库目录 link_directories(${THIRD_PARTY_PREFIX}/lib) # 2.增加第三方库头文…...

Django基础7——用户认证系统、Session管理、CSRF安全防护机制
文章目录 一、用户认证系统二、案例:登陆认证2.1 平台登入2.2 平台登出2.3 login_required装饰器 三、Django Session管理3.1 Django使用Session3.1.1 Cookie用法3.1.2 Session用法 3.2 案例:用户登录认证 四、Django CSRF安全防护机制 一、用户认证系统…...

基于流计算 Oceanus(Flink) CDC 做好数据集成场景
由于第一次做实时,所以踩坑比较多,见谅(测试环境用的flink),小公司没有用到hadoop组件 一、踩坑记录 1:本地代码的flink版本是flink1.15.4,生产环境是flink1.16.1,在使用侧输出流时报错,需要使用以下写法,需要使用Si…...

MySQL8.Xx安装控制台未生成随机密码解决方案
MySQL8.xx一主两从复制安装与配置 MySQL8.XX随未生成随机密码解决方案 MySQL8.0.30一主两从复制与配置(一) 一: Mysql 安装时控制台未生成密码 安装过程中解压或者安装时报错等,这种情况一般是因网络等其他原因导致下载的安装包不完整, 重新下载安装即可; 二:…...