SIGCHLD信号产生的3个条件:
1、子进程结束;
2、子进程暂停了;
3、子进程继续运行;
都会给父进程发送该信号,父进程默认忽略此信号。
接下来使用代码来解释这个问题:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/time.h>
4 #include <unistd.h>
5 #include <signal.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 void myfun(int num)
9 {
10 printf("catch you,id:%d\n",num);//获取捕捉的信号
11 while(1)
12 {
13 int ret=waitpid(-1,NULL,WNOHANG);//等待回收子进程
14 if(ret>0)
15 {//回收的子进程
16 printf("child die,id:%d\n",ret);//已经回收的子进程
17 }
18 else if(ret==0)//子进程已经获取完毕
19 {
20 break;
21 }
22 else if(ret==-1)//出现错误
23 {
24 break;
25 }
26 sleep(1);
27
28 }
29
30 }
31 int main()
32 {
33 pid_t pid;
34 for(int i=0;i<10;i++)
35 {
36 pid=fork();//创建10个子进程
37 if(pid==0)
38 {
39 //子进程
40 break;
41 }
42 }
43 if(pid>0)//父进程
44 {
45 struct sigaction act;
46 act.sa_flags=0;
47 act.sa_handler=myfun;
48 sigemptyset(&act.sa_mask);
49 int ret=sigaction(SIGCHLD,&act,NULL);//使用SIGCHLD解决僵尸问题
50 if(ret==-1)
51 {
52 perror("sigaction");
53 exit(2);
54 }
55 while(1)
56 {
57 printf("perent process id:%d\n",getpid());
58 sleep(1);
59 }
60 }
61 else if(pid==0)
62 {
63 printf("child process id:%d\n",getpid());
64 }
65
66 return 0;
67 }
其运行结果如下:
分析:在上述程序中,每个子进程在结束之前都会睡眠1s,这是为了留出足够的时间保证在所有子进程结束之前,父进程能够完成对SIGCHLD信号的注册(红色标1的地方)。否则所有子进程都结束了,父进程还没有完成注册,则此时信号都被忽略,从而子进程不能被父进程回收。但是,只要有一个子进程在信号注册后结束,所有子进程都可以被回收(红色标2的地方),因为使用了while循环回收子进程。红色标3的地方就是完成了父进程对子进程的回收。除了使用sleep函数来实现这一点外,其实更加高效而又精确的办法(其实在负载较大的 情况下,sleep函数也不能确保能够做到)就是在父进程注册函数之前就将SIGCHLD信号设置为阻塞(通过信号集操作函数加入到屏蔽字中),在注册完成时立即解除对该信号的阻塞即可。
接下来,我们一起来看通过信号集操作函数加入屏蔽字的相关代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/time.h>
4 #include <unistd.h>
5 #include <signal.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 void myfun(int num)
9 {
10 printf("catch you,id:%d\n",num);//获取捕捉的信号
11 while(1)
12 {
13 int ret=waitpid(-1,NULL,WNOHANG);//等待回收子进程
14 if(ret>0)
15 {//回收的子进程
16 printf("child die,id:%d\n",ret);//已经回收的子进程
17 }
18 else if(ret==0)//子进程已经获取完毕
19 {
20 break;
21 }
22 else if(ret==-1)//出现错误
23 {
24 break;
25 }
26 sleep(1);
27
28 }
29
30 }
31 int main()
32 {
sigset_t set;
34 sigemptyset(&set);
35 sigaddset(&set,SIGCHLD);
36 sigprocmask(SIG_BLOCK,&set,NULL);
37 //注册完信号捕捉之后,解除阻塞
38 sigprocmask(SIG_UNBLOCK,&set,NULL);
33 pid_t pid;
34 for(int i=0;i<10;i++)
35 {
36 pid=fork();//创建10个子进程
37 if(pid==0)
38 {
39 //子进程
40 break;
41 }
42 }
43 if(pid>0)//父进程
44 {
45 struct sigaction act;
46 act.sa_flags=0;
47 act.sa_handler=myfun;
48 sigemptyset(&act.sa_mask);
49 int ret=sigaction(SIGCHLD,&act,NULL);//使用SIGCHLD解决僵尸问题
50 if(ret==-1)
51 {
52 perror("sigaction");
53 exit(2);
54 }
55 while(1)
56 {
57 printf("perent process id:%d\n",getpid());
58 sleep(1);
59 }
60 }
61 else if(pid==0)
62 {
63 printf("child process id:%d\n",getpid());
64 }
65
66 return 0;
67 }
经过测试,与上面的运行结果一致。