一、进程等待的简介
(1)进程等待用来干什么的
我们通过之前学习过的知识知道了,如果一个进程如果在终止的时候会关闭它所有的文件描述符,同时释放在用户空间分配的内存,但是还保存着PCB等相关信息,与此同时还保存了一些其他的的相关信息。
如果此时进程正常终止,则其中保存着它的退出状态;如果是异常退出,则这些其他的相关信息会保存着该进程的终止信号是哪个。
这个时候我们的子进程的父进程可以调用wait和waitpid获取这些信息,然后根据这些信息来选择解决这个进程。
正常或异常终止发送信号
当一个进程正常或异常终止时,内核就向其父进程发送一个SIGCHLD信号。因为子进程终止是一个异步事件,所以发生这种信号也是内核向父进程发的异步通知。
父进程可以选择忽略该信号,或者提供一个该信号发生时即被调⽤用执⾏行的函数。对于这种信号的系统默认动作是忽略它。
查看进程退出状态
我们如果要查看一个进程的退出状态,则可以在Shell中使用‘$?’来查看进程的退出状态,因为shell是它的父进程,当它终止时shell调用wait或者waitpid得到这个子进程的退出状态,同时清除这个进程。
(2)为什么要有进程等待
我们通过进程等待来获取进程退出的时候的相关信息,来对进程进行一些处理。
(3)进程等待的必要性
- 子进程退出如果父进程不管子进程的任何资源,就可能产生“僵尸进程”,从而导致内存泄漏。
- 一个进程如果变成僵尸进程,那么即便我们用kill也没有没有办法杀掉它。(因为我们当然没有办法杀死一个已经死掉了的进程)
- 我们需要知道父进程分配给子进程的任务它完成的怎么样,是正常运行完成,还是运行出错,又或者是异常退出。
所以综上几点表明,父进程通过进程等待的方式,回收子进程的资源,同时来获取子进程退出的信息。
(4)调用wait与waitpid的进程可能会发生什么情况
- 如果其所有的进程都还在运行,则阻塞。
- 如果一个子进程已经终止,正在等待父进程获取其终止状态,则取得该子进程的终止状态立刻返回。
- 如果它没有任何子进程,则立即出错返回。
二、进程等待的方法
进程等待的方式主要有两个方法,一个是wait与waitpid。
wait与waitpid的区别
wait会令调用者阻塞直至某个子进程终止;
waitpid提供了wait函数不能实现的3个功能:
- waitpid等待参数指定的子进程, 而wait则返回任一终止状态的子进程;
- waitpid提供了一个wait的非阻塞版本;
- waitpid支持作业控制(以WUNTRACED选项). 用于检查wait和waitpid两个函数返回终止状态的宏: 这两个函数返回的子进程状态都保存在status指针中, 用以下3个宏可以检查该状态:
WIFEXITED(status):
若为正常终止, 则为真. 此时可执行 WEXITSTATUS(status): 取子进程传送给exit或_exit参数的低8位.
WIFSIGNALED(status):
若为异常终止, 则为真.此时可执行 WTERMSIG(status): 取使子进程终止的信号编号.
WIFSTOPPED(status):
若为当前暂停子进程, 则为真. 此时可执行 WSTOPSIG(status): 取使子进程暂停的信号编号
三、wait
我们首先来看下wait方法在Linux下的说明
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
- 返回值
我们可以看到,它的返回值类型是一个pid_t的类型。
它成功返回被等待进程的pid,失败则返回-1。 - 参数
输出型参数,获取子进程退出状态,不关心则可以设置成NULL。
进程由于收到了SIGCHLD而调用了wait,期望wait立刻返回,但是任意时刻调用wait,则进程可能会被阻塞。
四、waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
- 返回值:
- 当正常返回的时候waitpid返回收集到的子进程的进程ID;
- 如果设置了选项WNOHANG,而调用中waitpid发现已经没有可以退出的子进程可以收集,则返回0;
- 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
- 当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD.
- 参数:
- pid:
- pid=-1,等待任意一个子进程,与wait等效;
- pid>0, 等待其进程ID与pid相等的子进程。
- pid=0,等待其进程组ID等于调用进程组ID的任一个子进程。
- pid<-1,等待其进程组ID等于pid绝对值的任一个子进程。
- status:
- WIFEXITEN(status):若为正常终止子进程返回的状态,则为真。(一般用来查看进程是否正常退出)
- WEXITSTATUS(status): 若WEXITSTATUS非零,提取子进程退出码。(一般用来查看进程的退出码)
- options:
- WNOHANG:若pid为指定的子进程没有结束,则waitpid()函数返回0,不等待。若正常退出,则返回该子进程的ID。
- pid:
五、关于status参数
我们知道在wait与waitpid中都有status参数,他并不简简单单的是一个整型变量,因为父进程和子进程之间所有的状态交互都要通过这个参数来表示,因此这个int类型的status的若干bit位都有特殊含义的,所以它的编码比较重要。
所以status指出了子进程是正常退出还是异常退出的,以及正常结束时的返回值,或者是被哪一个信号结束或者进程的退出码是多少等信息,这些信息都被保存在这个参数的不同的二进制中,开发者通过设计一个专门的宏来完成了这个工作。