【Linux系统编程】Linux多进程介绍及使用

张开发
2026/4/7 8:26:04 15 分钟阅读

分享文章

【Linux系统编程】Linux多进程介绍及使用
Linux多进程介绍及使用1.相关的概念进程程序相关的shell命令进程的组成父进程孤儿进程(僵尸进程)2.linux提供跟进程有关的接口函数(1)进程的创建(创建子进程)fork函数的特点①一次调用两次返回分别表示父子进程示例代码fork返回值的理解② fork创建子进程的时候会给子进程分配独立的内存空间同时子进程会复制父进程的所有资源(会复制fork之前的变量(全局和局部))示例代码子进程创建成功会复制父进程的资源fork底层原理分析(2)进程的退出和回收exit和_exit的区别示例代码exit和_exit的区别return和exit的区别示例代码return和exit的区别进程的回收wait示例代码进程的退出与回收wait进程的回收waitpid示例代码waitpid指定回收子进程示例代码waitpid非阻塞等待(3)获取进程的id以及获取父进程的id示例代码创建多个子进程1示例代码创建多个子进程21.相关的概念进程一个正在运行的程序只要你运行了程序那么系统中都会产生一个对应的进程动态的概念程序用编译器编译得到的二进制文件静态的概念相关的shell命令ps -elf 查看当前系统进程的状态信息ps英文全称process state进程的组成代码块(程序代码)数据块(程序中用到的数据)进程控制块进程控制块(PCB–process contrl block)指的是系统中定义的一个结构体系统相关的头文件/usr/src/linux-hwe-5.15-headers-5.15.0-136/include/linux/sched.hstructtask_struct{//当./程序名运行一个程序的时候系统里面产生对应名字的进程并且把该进程在运行时候的状态参数全部存放到一个结构体变量中//这个结构体叫做struct task_struct(进程控制块 )//存放了跟当前进程有关的一些状态信息(比如:进程运行占用cpu百分比内存占用情况打开的文件信息)longstate//保存进程的运行状态-1表示没有运行0表示正在运行0表示暂停intpid;//进程的ID号longprority//进程的优先级}父进程\quad哪个主函数调用fork创建子进程该程序就是父进程intmain()//父进程{fork();//子进程}孤儿进程(僵尸进程)\quad父进程优先于子进程退出了导致子进程的资源没有回收这种现象就叫做孤儿进程2.linux提供跟进程有关的接口函数(1)进程的创建(创建子进程)#includeunistd.hpid_tfork();返回值:0表示父进程,此时返回值中存放的是子进程的id号0表示子进程-1表示失败fork函数的特点①一次调用两次返回分别表示父子进程示例代码fork返回值的理解#includestdio.h#includesys/types.h#includeunistd.hintmain(){pid_tpid;printf(程序开始运行\n);pidfork();if(pid0)//父进程{printf(父进程中的ID %d\n,pid);while(1){printf(父进程循环\n);sleep(1);}}elseif(pid0)//子进程{printf(子进程中的ID %d\n,pid);while(1){printf(子进程循环\n);sleep(1);}}else{printf(fork失败\n);return-1;}return0;}/* 执行结果 程序开始运行 父进程中的ID 3404 父进程循环 子进程中的ID 0 子进程循环 父进程循环 子进程循环 父进程循环 子进程循环 ... */② fork创建子进程的时候会给子进程分配独立的内存空间同时子进程会复制父进程的所有资源(会复制fork之前的变量(全局和局部))示例代码子进程创建成功会复制父进程的资源#includestdio.h#includesys/types.h#includeunistd.h/* 子进程创建成功子进程拥有独立的内存空间 讨论研究 子进程从它父进程那里里拷贝了什么拷贝的特点 特点1全局变量子进程会备份一个跟父进程的全局变量同名但是相互独立 特点2局部变量子进程会备份一个跟父进程的局部变量同名但是相互独立 */// 定义全局变量intg_num10;intmain(){pid_tpid;intm100;printf(程序开始运行\n);pidfork();if(pid0)//父进程{// 父进程修改全局变量子进程不会受影响g_num20;m200;printf(父进程中的ID %d\n,pid);while(1){printf(父进程循环 全局变量,g_num%d\n,g_num);printf(父进程循环 局部变量,m%d\n,m);sleep(1);}}elseif(pid0)//子进程{// 子进程修改全局变量父进程不会受影响g_num30;m300;printf(子进程中的ID %d\n,pid);while(1){printf(子进程循环 全局变量,g_num%d\n,g_num);printf(子进程循环 局部变量,m%d\n,m);sleep(1);}}else{printf(fork失败\n);return-1;}return0;}/* 执行结果 程序开始运行 父进程中的ID 3548 父进程循环 全局变量,g_num20 父进程循环 局部变量,m200 子进程中的ID 0 子进程循环 全局变量,g_num30 子进程循环 局部变量,m300 父进程循环 全局变量,g_num20 父进程循环 局部变量,m200 子进程循环 全局变量,g_num30 子进程循环 局部变量,m300 */fork底层原理分析(2)进程的退出和回收结束进程voidexit(intstatus);void_exit(intstatus);exit和_exit的区别exit结束进程的时候会刷新缓冲区但是_exit()不会刷新缓冲区正常情况下\n和return语句都能刷新缓冲区示例代码exit和_exit的区别#includemyhead.h/* 区别 exit会刷新缓冲区 _exit不会刷新缓冲区 相同都可以结束某个进程 */intmain(){printf(hello world!);//exit(0); //结束当前进程会刷新缓冲区因此打印语句可以见到_exit(0);//结束当前进程不会刷新缓冲区因此打印语句不能见到}return和exit的区别区别一return 是关键字exit()是函数区别二return结束函数返回返回值exit()结束整个进程示例代码return和exit的区别#includemyhead.h/* 区别 exit是个函数 return是个关键字 exit结束整个进程 return结束当前函数调用返回某个结果给到调用者 */intfun(){printf(fun这个函数被调用了!\n);//exit(0); //结束整个进程 当执行在exit函数时整个进程退出了不会执行后面的语句return0;//fun有个返回值是0}intmain(){printf(主函数开始运行!\n);fun();printf(调用fun完毕,主函数准备退出!\n);return0;}进程的回收wait#includesys/wait.hpid_twait(int*stat_loc);返回值成功 回收到的那个子进程的id号 失败-1参数(重点)stat_loc--》存放进程退出时候的状态信息 进程的退出值(exit/_exit圆括号里面的整型参数)仅仅只是状态信息的一部分状态信息还包含其它内容(子进程是正常退出还是异常退出是被哪个信号终止的)特点阻塞父进程等待子进程的退出补充kill -9 进程的ID号 //给指定的进程发送9号信号kill -l //罗列linux系统中所有的信号示例代码进程的退出与回收wait#includestdio.h#includesys/types.h#includeunistd.h#includesys/wait.h#includestdlib.hintmain(){pid_tpid;intstatus;printf(程序开始运行\n);pidfork();// 创建子进程if(pid0)//父进程{printf(父进程执行,父进程id%d,子进程id%d\n,getpid(),pid);}elseif(pid0)//子进程{printf(子进程执行,子进程id%d\n,getpid());exit(5);//退出子进程 如不加exit退出子进程运行会提示子进程异常退出}else{perror(fork失败\n);return-1;}printf(父进程准备退出\n);// 回收新建的子进程pid_tretwait(status);//判断子进程是正常退出还是异常退出if(WIFEXITED(status)){printf(子进程是正常退出的,退出值是: %d!\n,WEXITSTATUS(status));printf(%d\n,status);}elseprintf(子进程是异常退出的!\n);printf(父进程回收子进程成功,回收的子进程ID是: %d\n,ret);return0;}/* 执行结果 // 屏蔽exit()函数的情况 程序开始运行 父进程执行,父进程id3323,子进程id3324 父进程准备退出 子进程执行,子进程id3324 父进程准备退出 子进程是异常退出的! 父进程回收子进程成功,回收的子进程ID是: -1 子进程是正常退出的,退出值是: 0! 0 父进程回收子进程成功,回收的子进程ID是: 3324 // 未屏蔽exit()函数的情况 程序开始运行 父进程执行,父进程id3339,子进程id3340 父进程准备退出 子进程执行,子进程id3340 子进程是正常退出的,退出值是: 5! 1280 父进程回收子进程成功,回收的子进程ID是: 3340 */进程的回收waitpidpid_twaitpid(pid_tpid,int*stat_loc,intoptions);返回值成功 回收到的那个子进程的id号 失败-1参数pid--》-1回收进程组id是pid绝对值中的某个进程 比如waitpid(-10000,)回收进程组ID是10000的这个组里面的某个进程-1回收任意一个进程 比如waitpid(-1,);0回收本进程组中的某个进程-跟主函数在同一组内的进程 比如waitpid(0)0指定回收进程id是pid的这个进程 比如:waitpid(10000,);回收id是10000的这个进程 options--》 WNOHANG 非阻塞等待父进程在退出的时候如果子进程还没有退出那么父进程不会阻塞也不会去回收子进程直接退出0阻塞等待 补充进程组就是多个进程组成的一个集合 linux允许多个进程加入到某个组里面形成了一个小团队--》进程组示例代码waitpid指定回收子进程#includestdio.h#includesys/types.h#includeunistd.h#includesys/wait.h#includestdlib.h/* waitpid指定回收子进程 */intmain(){pid_tid,id1,id2;pid_tret;inti;intstatus;//先生第一个idfork();if(id0)//父进程{//生第二个id1fork();if(id10)//第二个子进程{printf(扫描漏洞!\n);while(1){// 此时父进程会阻塞在这里等待子进程结束}exit(2);}elseif(id10)//父进程{//生第三个子进程id2fork();if(id20)//第三个子进程{printf(清理垃圾!\n);exit(3);}}}elseif(id0)//第一个子进程{printf(杀毒!\n);exit(1);}printf(父进程执行软件升级!\n);//回收三个子进程--假设我按照 2 3 1来回收retwaitpid(id1,status,0);// 0:阻塞等待 WNOHANG:非阻塞printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);retwaitpid(id2,status,0);printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);retwaitpid(id,status,0);printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);return0;}/* 执行结果 杀毒! 父进程执行软件升级! 扫描漏洞! 清理垃圾! // 此时父进程会阻塞在这里等待子进程结束 */示例代码waitpid非阻塞等待#includemyhead.h/* waitpid非阻塞等待 */intmain(){pid_tid,id1,id2;pid_tret;inti;intstatus;//先生第一个idfork();if(id0)//父进程{//生第二个id1fork();if(id10)//第二个子进程{printf(扫描漏洞!\n);sleep(2);exit(2);}elseif(id10)//父进程{//生第三个子进程id2fork();if(id20)//第三个子进程{printf(清理垃圾!\n);exit(3);}}}elseif(id0)//第一个子进程{printf(杀毒!\n);exit(1);}printf(父进程执行软件升级!\n);//回收三个子进程--假设我按照 2 3 1来回收retwaitpid(id1,status,WNOHANG);printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);retwaitpid(id2,status,0);printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);retwaitpid(id,status,0);printf(我目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);return0;}具体宏定义如下(3)获取进程的id以及获取父进程的id获取当前进程的id号pid_tgetpid(void);获取父进程的idpid_tgetppid(void);示例代码创建多个子进程1#includestdio.h#includesys/types.h#includeunistd.h#includesys/wait.h#includestdlib.hintmain(intargc,charconst*argv[]){pid_tpid1,pid2;intret,status;//创建两个子进程//错误写法1 实际结果创建出来的不止两个子进程//fork();//fork();//错误写法2 实际结果创建出来的不止两个子进程//for(int i0; i2; i)//fork();pid1fork();if(pid10)//父进程{printf(This is parent process1\n);// 创建第二个子进程pid2fork();if(pid20)//父进程{printf(This is parent process2\n);}elseif(pid20)//子进程2{printf(This is child process 2\n);exit(2);}}elseif(pid10)//子进程1{printf(This is child process 1\n);exit(1);}printf(父进程执行!\n);//回收两个子进程retwait(status);// printf(目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);printf(目前回收的子进程是: %d,退出值: %d\n,ret,WEXITSTATUS(status));retwait(status);// printf(目前回收的子进程是: %d,退出值: %d\n,ret,(status0xff00)8);printf(目前回收的子进程是: %d,退出值: %d\n,ret,WEXITSTATUS(status));return0;}/* 执行结果 This is parent process1 This is parent process2 父进程执行! This is child process 1 This is child process 2 目前回收的子进程是: 4978,退出值: 1 目前回收的子进程是: 4979,退出值: 2 */示例代码创建多个子进程2#includestdio.h#includeunistd.h#includesys/wait.h#includestdlib.h#defineN5intmain(){pid_tchildren[N];// 记录子进程 PID以便精确回收for(inti0;iN;i){pid_tpidfork();if(pid-1){perror(fork);exit(1);}elseif(pid0){// 子进程工作printf(子进程 %d (PID%d) 工作中...\n,i1,getpid());sleep(1);// 模拟工作exit(i1);// 退出码为 i1}else{children[i]pid;// 父进程保存子进程 PID}}// 父进程回收所有子进程并收集退出状态for(inti0;iN;i){intstatus;pid_tpidwaitpid(children[i],status,0);if(WIFEXITED(status)){printf(子进程 %d (PID%d) 正常退出退出码 %d\n,i1,pid,WEXITSTATUS(status));}}printf(所有子进程已回收父进程结束。\n);return0;}/* 执行结果 子进程 1 (PID3057) 工作中... 子进程 3 (PID3059) 工作中... 子进程 2 (PID3058) 工作中... 子进程 5 (PID3061) 工作中... 子进程 4 (PID3060) 工作中... 子进程 1 (PID3057) 正常退出退出码 1 子进程 2 (PID3058) 正常退出退出码 2 子进程 3 (PID3059) 正常退出退出码 3 子进程 4 (PID3060) 正常退出退出码 4 子进程 5 (PID3061) 正常退出退出码 5 所有子进程已回收父进程结束。 */

更多文章