SIGCHLD的产生条件
1、子进程终止时 2、子进程接收到SIGSTOP信号停止时 3、子进程处在停止态,接受到SIGCONT后唤醒时
源代码signal_test.c
void handle_sig_child() { int status; pid_t pid; pid = waitpid(-1, &status, WUNTRACED | WCONTINUED); printf("recv child pid %d \n", pid); if(WIFEXITED(status)) printf("child process exited with %d\n", WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("child process signaled with %d\n", WTERMSIG(status)); else if(WIFSTOPPED(status)) printf("child process stoped\n"); else if(WIFCONTINUED(status)) printf("child process continued\n"); } int main(int argc, char** argv) { pid_t pid; /*捕捉SIGCHLD信号*/ signal(SIGCHLD, handle_sig_child); pid = fork(); if(pid == 0) { sleep(5); printf("child PID [%d]\n", getpid()); exit(0); } else if(pid != -1) { while(1) sleep(1); } else { printf("fork error\n"); } }
运行结果:
where@ubuntu:~$ ./sigal_child_test child PID [9881] child PID [9881] return [0]
单个子进程的时候可以这么处理,但是如果有多个子进程,如果多个子进程在极短的时间类同时退出产生SIGCHLD信息,那么由于未决信号集不支持排队,有可能有些信号就不执行了。看下面的例子:
void handle_sig_child() { int status; pid_t pid; pid = waitpid(-1, &status, WUNTRACED | WCONTINUED); printf("recv child pid %d \n", pid); if(WIFEXITED(status)) printf("child process exited with %d\n", WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("child process signaled with %d\n", WTERMSIG(status)); else if(WIFSTOPPED(status)) printf("child process stoped\n"); else if(WIFCONTINUED(status)) printf("child process continued\n"); } int main(int argc, char** argv) { pid_t pid; /*捕捉SIGCHLD信号*/ signal(SIGCHLD, handle_sig_child); int count = 0; AGAIN: pid = fork(); //fork十次 if(pid == 0) { sleep(5); printf("child PID [%d]\n", getpid()); exit(0); } if(++count < 10) goto AGAIN; while(1) sleep(1); }
运行结果:
$ ./a.out child PID [7785] child PID [7784] child PID [7783] child PID [7782] child PID [7781] child PID [7787] child PID [7788] child PID [7780] child PID [7779] child PID [7786] recv child pid 7779 child process exited with 0 recv child pid 7780 child process exited with 0 recv child pid 7781 child process exited with 0
上面的结果产生十个子进程,退出后产生十个SIGCHLD,但是只执行了3次信号处理函数。
$ ps -ef where 7778 3197 0 18:34 pts/2 00:00:00 ./a.out where 7782 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7783 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7784 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7785 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7786 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7787 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7788 7778 0 18:34 pts/2 00:00:00 [a.out] <defunct> where 7790 4193 0 18:34 pts/18 00:00:00 ps -ef
出现未回收的情况,我们需要改进一下回收函数。
void handle_sig_child() { int status; pid_t pid; do { pid = waitpid(-1, &status, WUNTRACED | WCONTINUED | WNOHANG); printf("recv child pid %d \n", pid); if(WIFEXITED(status)) printf("child process exited with %d\n", WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("child process signaled with %d\n", WTERMSIG(status)); else if(WIFSTOPPED(status)) printf("child process stoped\n"); else if(WIFCONTINUED(status)) printf("child process continued\n"); }while(pid != -1); } int main(int argc, char** argv) { pid_t pid; /*捕捉SIGCHLD信号*/ signal(SIGCHLD, handle_sig_child); int count = 0; AGAIN: pid = fork(); if(pid == 0) { sleep(5); printf("child PID [%d]\n", getpid()); exit(0); } if(++count < 10) goto AGAIN; while(1) sleep(1); }
运行结果:
$ ./a.out child PID [7851] child PID [7852] child PID [7847] child PID [7850] child PID [7849] child PID [7853] child PID [7854] child PID [7855] child PID [7856] child PID [7848] recv child pid 7847 child process exited with 0 recv child pid 7848 child process exited with 0 recv child pid 7849 child process exited with 0 recv child pid 7850 child process exited with 0 recv child pid 7851 child process exited with 0 recv child pid 7852 child process exited with 0 recv child pid 7853 child process exited with 0 recv child pid 7854 child process exited with 0 recv child pid 7855 child process exited with 0 recv child pid 7856 child process exited with 0 recv child pid -1 child process exited with 0 recv child pid -1 child process exited with 0
十个子进程都完美回收。