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改为ifif (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信号,故回收一次。子进程回收不完全。