2.2.5 回收子进程
孤儿进程与僵尸进程
孤儿进程
:父进程
结束、子进程
尚未结束,此时子进程
为孤儿进程
,其父进程
归为init进程
(PID
为1
)。孤儿进程
虽然脱离父进程
控制,但是会自然结束,通常不具有危害。僵尸进程
:子进程
结束、父进程
尚未结束,如果父进程
未处理子进程
的结束信息,未回收子进程
的内核区数据,此时子进程
为僵尸进程
。在进程概述中讲到,子进程
结束不会自动释放内核区数据,等待父进程
接收并处理其结束信息。如果处于将死状态的子进程
一直存在,将不断占用内核区内存和PID
,大量积累具有危害。父进程
不处理僵尸进程
,在其结束后,init
进程会为其处理僵尸进程
。但是如果父进程
是长期运行的进程(如服务器进程),危害将持续积累。wait
函数与waitpid
函数用于处理和回收子进程
内核区数据。
wait 函数回收子进程
-
wait
函数与waitpid
函数用于接收进程状态变化,默认阻塞
进程等待任意子进程
状态变化。 -
返回值
:当任意进程状态变化,函数返回子进程PID
,设置wstatus
值表示该子进程
状态变化编码(后述情况不设置wstatus
);当WNOHANG
选项打开,并且存在未结束子进程
,返回0
;当所有子进程
已回收,抛出No child process
错误,返回-1
;当出现其他错误,返回-1
。 -
pid
:分为下述情况:< -1
:等待任意组ID
为-pid
(pid
的绝对值)的子进程
状态变化。-1
:等待任意子进程
状态变化。0
:等待任意组ID
与函数启动时父进程
的组ID
相同的子进程
状态变化。> 0
:等待进程ID
为pid
的子进程
状态变化。- 如果对应进程符号
ID
要求但不是子进程
(子孙进程
不属于子进程
)的,不属于控制范围。
-
wstatus
:整型指针,用于接收状态变化编码,可为NULL
表示不接收信息。解码宏如下:WIFEXITED(wstatus)
:返回01整型。是否正常退出(exit
为正常退出)。WEXITSTATUS(wstatus)
:返回整型。如果上宏为真,返回退出状态(exit
参数,0~255
)。WIFSIGNALED(wstatus)
:返回01整型。是否异常退出(信号退出为异常退出)。WTERMSIG(wstatus)
:返回整型。如果上宏为真,返回引发退出的信号。WIFSTOPPED(wstatus)
:返回01整型。是否进入暂停。WSTOPSIG(wstatus)
:返回整型。如果上宏为真,返回引发暂停的信号。WIFCONTINUED(wstatus)
:返回01整型。是否进程继续。
-
options
:宏如下:(使用|
连接)(默认)
:等待进程正常退出、进程异常退出(与被追踪进程暂停)。WNOHANG
:不阻塞,立即返回。WUNTRACED
:等待进程(未被追踪进程)被暂停。WCONTINUED
:等待进程被继续。
-
wait(wstatus)
等价于waitpid(-1, wstatus, 0)
。 -
关于更多:
$ man 2 wait
。
#include <sys/types.h>
#include <sys/wait.h>
// wait for the change state
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
#include <stdio.h>
// print error message if return value is -1
perror("wait");
信号回收子进程
- 我们注意到
wait
函数回收子进程时,需要主动检测是否有子进程状态变化;如果我们希望即使处理子进程,必须阻塞的回收子进程,或者使用独立线程处理子进程。在2.3.4 信号章节中将提到,子进程状态变化时将触发SIGCHLD
信号,届时可以考虑使用信号即时回收子进程。
// 请先了解 2.3.4 信号 章节后阅读此代码
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void process_signal(int sig) {
if (sig == SIGCHLD) {
int ret = 1, wstatus;
while (ret > 0) {
waitpid(-1, &wstatus, WNOHANG);
// 业务逻辑
}
}
}