用wait和waitpid函数清理僵尸过程,父亲的过程可以阻止等待子过程结束,也可以查询是否有子过程结束等待清理(即轮询)。在第一种方式下,如果父亲的过程被阻塞,他就无法处理自己的工作;在第二种方式下,父亲的过程应该记住在处理自己的工作时不时地轮流询问。程序很复杂。
事实上,当子过程终止时,发送给父亲SIGCHLD该信号的默认处理动作被忽略,父亲的过程可以自定义SIGCHLD信号处理函数,所以父亲的过程只需要专注于自己的工作,不需要关心子过程,子过程终止将通知父亲的过程,父亲的过程在信号处理函数中调用wait清理子过程即可。
编写个程序:父过程:fork出子过程,子过程调用exit(2)终止,自定义父亲的过程SIGCHLD其中调用信号处理函数wait获取子过程的退出状态并打印。
#include "./common/head.h" /*功能: *子过程正常死亡(exit),发出SIGCHLD信号;父亲的过程绑定了信号的处理函数,并收集了它。 */ ///父进程给子进程收尸的处理函数 void sig_child(int signo){ int sts; pid_t pid = wait(&sts); if(WIFEXITED(sts)){ ///子过程正常死亡 printf("proc:%d exit with code %d\n", pid, WEXITSTATUS(sts)); } return ; } int main() { pid_t pid = fork(); if(pid < 0){ perror("fork"); exit(1); } if(pid){ //父进程 printf("child pid = %d\n", pid); /// struct sigaction newact, oldact; newact.sa_handler = sigchild; sigemptyset(newact.sa_mask); newact.sa_flags = 0; sigaction(SIGCHLD, &newact, &oldact); ///父亲一直在工作 int n = 10; while(n--){ printf("work~\n"); sleep(1); } }else{ //子进程 sleep(2); exit(2); } return 0; }
事实上,因为UNIX由于历史原因,还有另一种方法可以避免僵尸过程:父亲过程调用sigaction将SIGCHLD处理动作为SIG_IGN,这样fork子过程在终止时会自动清,不会产生僵尸流程,也不会通知父流程。此方法对于Linux可用,但不保证在其他地方UNIX可用于系统。编程验证不会产生僵尸过程。
#include "./common/head.h" /*功能: *父进程将SIGCHLD设置信号的处理函数SIG_IGN,子过程正常死亡(exit),这时子进程会自动清理自己的尸体,不会发送SIGCHLD。 */ ///父进程给子进程收尸的处理函数 void sig_child(int signo){ int sts; pid_t pid = wait(&sts); if(WIFEXITED(sts)){ ///子过程正常死亡 printf("proc:%d exit with code %d\n", pid, WEXITSTATUS(sts)); } return ; } int main() { pid_t pid = fork(); if(pid < 0){ perror("fork"); exit(1); } if(pid){ //父进程 printf("child pid = %d\n", pid); /// struct sigaction newact, oldact; newact.sa_handler = SIG_IGN; ///父进程对SIGCHLD设置信号处理动作SIG_IGN以后,子过程会自动处理自己的尸体,不会产生僵尸过程,也不会通知父亲过程。 sigemptyset(newact.sa_mask); newact.sa_flags = 0; sigaction(SIGCHLD, &newact, &oldact); ///父亲一直在工作 int n = 10; while(n--){ printf("work~\n"); sleep(1); } }else{ //子进程 sleep(2); exit(2); } return 0; }