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 ", pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("child %d cancel signal %d ", 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 ", 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 ", 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 ", pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("child %d cancel signal %d ", pid, WTERMSIG(status)); } }
此写法会导致子进程回收不完全,原因:在执行信号处理函数时,多个子进程同时死亡,产生多个SIGCHLD信号。但由于函数正在执行故屏蔽SIGCHLD,但执行完成后未决信号集中只记录一次SIGCHLD信号,故回收一次。子进程回收不完全。