SIGCHLD的产生条件
子进程终止时
子进程接收到SIGSTOP信号停止时
子进程处在停止态,接受到SIGCONT后唤醒时
借助SIGCHLD信号回收子进程
子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void sys_err(char *str)
{
perror(str);
exit(1);
}
void do_sig_child(int signo)
{
int status; pid_t pid;
while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
if (WIFEXITED(status))
printf("child %d exit %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
}
}
int main(void)
{
pid_t pid; int i;
for (i = 0; i < 10; i++) {
if ((pid = fork()) == 0)
break;
else if (pid < 0)
sys_err("fork");
}
if (pid == 0) {
int n = 1;
while (n--) {
printf("child ID %d\n", getpid());
sleep(1);
}
return i+1;
} else if (pid > 0) {
//这里还应对SIGCHLD进行阻塞 防止父进程SIGCHLD还未注册完成子进程就已经死亡
struct sigaction act;
act.sa_handler = do_sig_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
//解除阻塞
while (1) {
printf("Parent ID %d\n", getpid());
sleep(1);
}
}
return 0;
}
上述代码若将do_sig_child(),SIGCHLD信号处理函数改为:
void do_sig_child(int signo)
{
int status; pid_t pid;
if((pid = waitpid(0, &status, WNOHANG)) > 0) { //将while改为if
if (WIFEXITED(status))
printf("child %d exit %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
}
}
此写法会导致子进程回收不完全,原因:在执行信号处理函数时,多个子进程同时死亡,产生多个SIGCHLD信号。但由于函数正在执行故屏蔽SIGCHLD,但执行完成后未决信号集中只记录一次SIGCHLD信号,故回收一次。子进程回收不完全。