🌈个人主页:Yui_
🌈Linux专栏:Linux
🌈C语言笔记专栏:C语言笔记
🌈数据结构专栏:数据结构
🌈C++专栏:C++
文章目录
1. 为什么需要进行进程等待
进程等待是多进程编程中至关重要的一部分,主要原因是为了让父进程正确管理子进程生命周期并避免各种问题。
- 进行资源回收,当子进程结束后,操作系统不会立即释放与该进程相关的所有资源,需要父进程来获取子进程的终止状态,并释放这些资源。
- 避免僵尸进程,虽然子进程已经结束运行,但是它在进程中仍然回保留占位条目,需要父进程回收。
- 获取子进程的退出状态,用来判断子进程是否成功执行完成任务
2. 进程等待的方法
2.1 wait方法
头文件
#include<sys/types.h>#include<sys/swit.h>
语法格式
pid_twait(int*status);
放回值:
成功返回被等待进程的pid,失败返回-1
参数:
输出型参数,获取子进程的退出状态,不甘心则可以设置为NULL
测试代码:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<stdlib.h>#include<sys/wait.h>intmain(){pid_t id =fork();if(id <0){perror("fork error");exit(-1);//错误情况 }if(id ==0){//child process int cnt =5;while(cnt--){printf("i am a child process pid:%d\n",getpid());;sleep(1);}exit(0);//process exit }sleep(10);//wait child process printf("i am a father pid:%d\n",getpid());pid_t ret =wait(NULL);if(ret >0)printf("father wait:%d,success\n", ret);else("father wait failed\n");sleep(10);return0;}
查看进程状态:
ubuntu@VM-20-9-ubuntu:~/processWait$ while:;dops axj |head -1 &&ps axj |grep process|grep -v grep;sleep1;echo"#######################################";donePPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 Z+ 10000:00 [process]<defunct>#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 Z+ 10000:00 [process]<defunct>#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 Z+ 10000:00 [process]<defunct>#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 Z+ 10000:00 [process]<defunct>#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
668279668280668279651091 pts/0 668279 Z+ 10000:00 [process]<defunct>#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
651091668279668279651091 pts/0 668279 S+ 10000:00 ./process
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
#######################################PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
查看上面的情况,我们可以发现,在中间有段时间子进程进入了僵尸状态,后来就被父进程给回收了。
2.2 waitpid方法
头文件:
#include<sys/types.h>#include<sys/swit.h>
语法格式:
pid_twaitpid(pid_t pid,int* status,int options);
返回值:
当正常放回的时候waitpid返回搜集到的子进程进程ID
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
如果调用中出错,则返回-1,这时error会被设置成相对应的值以指示错误所在。
参数:
pid:
pid=-1,等待任意一个子进程,与wait等效。
pid>0,等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status):如果正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXISTATUS(status):如果WIFEXIED非0,提取子进程退出码。(查看进程退出码)
options:
WNOHANG:如果pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待,如果正常结束,则返回该子进程的ID。
- 如果子进程已经退出,调用wait/waitpid会立即返回,并且释放资源,获取子进程退出信息。
- 如果任意时刻调用wait/waitpid,子进程存在且正常运行,则可能阻塞。
- 如果不存在该进程,则立即出错放回。
2.3 获取子进程status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。
- 如果不传递NULL,操作系统会会根据该参数,将子进程的退出信息反馈给父进程。
- status不能简单的当作整型来看,应该当作位图来看待。
测试代码:
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/wait.h>#include<sys/types.h>intmain(){pid_t id =fork();if(id<0){perror("fork fail");exit(1);}if(id ==0){//childprintf("i am a child pid:%d\n",getpid());sleep(20);exit(10);}else{//fatherint status;int ret =wait(&status);if(ret>0&&(status&0x7F)==0){//normal exitprintf("child exit code:%d\n",(status>>8)&0xFF);}elseif(ret>0){printf("sig code:%d\n",status&0x7F);}}return0;}
正常退出会打印:child exit code:10
运行过程中被kill掉:sig code:9
2.4 进程的堵塞等待方式
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/wait.h>#include<sys/types.h>intmain(){pid_t id =fork();if(id <0){perror("fork fail");exit(1);}elseif(id ==0){//childprintf("i am a child,pid is:%d\n",getpid());sleep(5);exit(257);}else{//fatherint status =0;pid_t ret =waitpid(-1,&status,0);//阻塞等待printf("This is test for wait\n");if(WIFEXITED(status)&&ret == id){printf("wait child 5s success,child return code is:%d\n",WEXITSTATUS(status));}else{printf("wait child failed,return\n");return1;}}return0;}
执行过程:
因为阻塞等待的缘故,在子进程结束前父进程都不会执行下一条语句。会一直卡在
pid_t ret = waitpid(-1,&status,0);
者句语句。
执行结果:
i am a child,pid is:701810
This is test for wait
wait child 5s success,child return code is:1
提问:为什么会放回1呢
回答:
在上面的图中,我们得知了但程序正常退出后,其退出状态存储在低8位中,而257的二进制位为
0001 0000 0001
,会被截断为1
2.5 进程的非堵塞等待方法
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/wait.h>#include<sys/types.h>intmain(){pid_t id =fork();if(id <0){perror("fork fail");exit(1);}elseif(id ==0){//childprintf("i am a child,pid is:%d\n",getpid());sleep(5);exit(1);}else{//fatherint status =0;pid_t ret =0;do{
ret =waitpid(-1,&status,WNOHANG);//非阻塞等待if(ret ==0){printf("child is running\n");}sleep(1);}while(ret ==0);if(WIFEXITED(status)&&ret == id){printf("wait child 5s success,child return code is:%d\n",WEXITSTATUS(status));}else{printf("wait child failed,return\n");return1;}}return0;}
执行结果:
child is running
i am a child,pid is:711095
child is running
child is running
child is running
child is running
wait child 5s success,child return code is:1
从这两段代码大家肯定可以分的清楚堵塞和非堵塞的区别了吧。就是还不理解,下面我在来一个例子相信大家一定就没有问题了。
3.解释堵塞与非堵塞
- 阻塞场景:打电话等朋友接听- 你拨打朋友的电话,直到朋友接通之前你什么都做不了。这就像阻塞调用,你必须等着事情完成。
- 非阻塞场景:发消息等待回复- 你给朋友发了个消息,等他们回你。你不用一直盯着手机看,而是可以去做别的事情,等收到消息后再查看。这就像非阻塞调用,你不需要等着完成才能做其他事情。
就是如此,感谢观看本篇文文章,提前感谢大家的点赞与收藏~
版权归原作者 Yui_ 所有, 如有侵权,请联系我们删除。