背景

我们知道,当一个进程fork出子进程后,没有对子进程进行回收,那么子进程运行完之后的状态会变为僵尸进程.

我们可以通过wait和waitpid来回收子进程,防止僵尸进程的出现.

但是wait和waitpid,要么以阻塞方式运行,要么以轮询方式运行,都极大的占用了CPU资源.

本文将介绍,父进程如何通过异步操作回收子进程!

原理

在每个子进程运行完成之后,都会向父进程发出SIGCHLD信号.而在默认情况下,父进程会忽略掉该信号.因此,我们只需要对SIGCHLD信号进行捕捉,即可异步对子进程进行回收.

核心代码1

1
2
3
4
5
6
7
8
void waitChild1(int sig){
int status = 0;
if(waitpid(-1,&status,WNOHANG)>0){
printf("the child exit status is %d\n",WEXITSTATUS(status));
}else{
printf("failure!\n");
}
}

因此,我们通过waitpid,对所有产生SIGCHLD信号的子进程都进行回收.

好像很简单的样子嘛!!!

对的,就是这么简单!

但是上述代码存在BUG,如果存在很多子进程都同时退出,那么就会同时向父进程发送信号.

而我们知道,由于受到位图的限制,常规信号出现多次时,只计算一次.因此,如果同时存在子进程出现时,以上代码肯定不能把所有子进程都回收掉!

所以,我们需要对代码进行加强!!!

升级版核心代码

1
2
3
4
5
6
7
void waitChild2(int sig){
int status = 0;
pid_t pid = 0;
while((pid = waitpid(-1,&status,WNOHANG)) >0){
printf("%d's exit status is %d\n",pid,WEXITSTATUS(status));
}
}

正如上述代码,改动的其实很简单,只需把if改成while即可!

完整版代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
////////////////////////////////////
//文件说明:wait.c
//作者:高小调
//创建时间:2017年06月27日 星期二 20时45分16秒
//开发环境:Kali Linux/g++ v6.3.0
////////////////////////////////////
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
void waitChild1(int sig){
int status = 0;
if(waitpid(-1,&status,WNOHANG)>0){
printf("the child exit status is %d\n",WEXITSTATUS(status));
}else{
printf("failure!\n");
}
}
void waitChild2(int sig){
int status = 0;
pid_t pid = 0;
while((pid = waitpid(-1,&status,WNOHANG)) >0){
printf("%d's exit status is %d\n",pid,WEXITSTATUS(status));
}
}
int main(){
signal(SIGCHLD,waitChild2);
int pid = fork();
if(pid == 0){
//child
printf("i am child,my pid is %d\n",getpid());
exit(1);
}
pid = fork();
if(pid == 0){
//child
printf("i am child,my pid is %d\n",getpid());
exit(1);
}
pid = fork();
if(pid == 0){
//child
printf("i am child,my pid is %d\n",getpid());
exit(1);
}
pid = fork();
if(pid == 0){
//child
printf("i am child,my pid is %d\n",getpid());
exit(1);
}
while(1){
printf("i am father,my pid is %d\n",getpid());
sleep(5);
}
return 0;
}