SIGCHLD

产生SIGCHLD信号的条件

  • 子进程结束的时候

  • 子进程收到SIGSTOP信号

  • 当子进程停止时,收到SIGCONT信号

SIGCHLD信号的作用

​ 子进程退出后,内核会给它的父进程发送SIGCHLD信号,父进程收到这个信号后可以对子进程进行回收。

​ 使用SIGCHLD信号完成对子进程的回收可以避免父进程阻塞等待而不能执行其他操作,只有当父进程收到SIGCHLD信号之后才去调用信号捕捉函数完成对子进程的回收,未收到SIGCHLD信号之前可以处理其他操作。

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
//对SIGCHLD信号测试
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>

void sighandler(int signo)
{
printf("signo==[%d]\n",signo);
}

int main(int argc,char * argv[])
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
}

if(pid == 0)
{
printf("child [%d]\n",pid);
while(1)
{
sleep(1);
}
}
else if(pid > 0)
{
signal(SIGCHLD,sighandler);
printf("fater [%d]\n",pid);
while(1)
{
sleep(1);
}
}



return 0;
}
1
2
kill -19 子进程id号 	//对子进程发送SIGSTOP信号,然后产生SIGCHLD信号
kill -18 子进程id号 //对子进程发送SIGCONT信号,然后产生SIGCHLD信号

使用SIGCHLD信号完成对子进程的回收

  • 练习:父进程创建三个子进程,然后让父进程捕获SIGCHLD信号完成对子进程的回收。

  • 注意点:

  • 有可能还未完成信号处理函数的注册三个子进程都退出了。

  • 解决办法:可以在fork之前先将SIGCHLD信号阻塞,当完成信号处理函数的注册后在解除阻塞。

  • 当SIGCHLD信号函数处理期间, SIGCHLD信号若再次产生是被阻塞的,而且若产生了多次, 则该信号只会被处理一次, 这样可能会产生僵尸进程。

  • 解决办法: 可以在信号处理函数里面使用while(1)循环回收, 这样就有可能出现捕获一次SIGCHLD信号但是回收了多个子进程的情况,从而可以避免产生僵尸进程。

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//父进程使用SICCHLD信号完成对子进程的回收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void waitchild(int signo)
{
pid_t wpid;

//回收子进程
while(1)
{
wpid = waitpid(-1, NULL, WNOHANG);
if(wpid>0)
{
printf("child is quit, wpid==[%d]\n", wpid);
}
else if(wpid==0)
{
printf("child is living, wpid==[%d]\n", wpid);
break;
}
else if(wpid==-1)
{
printf("no child is living, wpid==[%d]\n", wpid);
break;
}
}
}

int main()
{
int i = 0;
int n = 3;

//将SIGCHLD信号阻塞
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);

for(i=0; i<n; i++)
{
//fork子进程
pid_t pid = fork();
if(pid<0) //fork失败的情况
{
perror("fork error");
return -1;
}
else if(pid>0) //父进程
{
printf("father: fpid==[%d], cpid==[%d]\n", getpid(), pid);
sleep(1);
}
else if(pid==0) //子进程
{
printf("child: fpid==[%d], cpid==[%d]\n", getppid(), getpid());
break;
}
}

//父进程
if(i==3)
{
printf("[%d]:father: fpid==[%d]\n", i, getpid());
//signal(SIGCHLD, waitchild);
//注册信号处理函数
struct sigaction act;
act.sa_handler = waitchild;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sleep(5);
sigaction(SIGCHLD, &act, NULL);

//解除对SIGCHLD信号的阻塞
sigprocmask(SIG_UNBLOCK, &mask, NULL);

while(1)
{
sleep(1);
}
}

//第1个子进程
if(i==0)
{
printf("[%d]:child: cpid==[%d]\n", i, getpid());
//sleep(1);
}

//第2个子进程
if(i==1)
{
printf("[%d]:child: cpid==[%d]\n", i, getpid());
sleep(1);
}

//第3个子进程
if(i==2)
{
printf("[%d]:child: cpid==[%d]\n", i, getpid());
sleep(1);
}

return 0;
}