/* OS: CentOS
* gcc avoid_zombie.c -o avoid_zombie
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
int ret=0;
pid_t pid;
//signal(SIGCHLD, SIG_IGN); //可以避免子进程成为僵尸进程
pid = fork(); // pid=子进程的进程号
if( pid < 0 )
{
perror("fail to fork");
exit(1);
}
else if( pid == 0 ) //child process(pid)
{
int pid2;
pid2 = fork();
if( pid2 < 0 ) {
perror("fail to fork");
exit(1);
}
else if( pid2 == 0 ) { // grandson process(ppid == pid), do some job.
printf("grandson process begin, pid=%d, ppid=%d\n", getpid(),getppid());
sleep(10);
// do some job... 要做的工作放在孙进程执行
printf("grandson process exit, pid=%d, ppid=%d\n", getpid(),getppid());
exit(0);
}
else { // child process(pid), exit.
sleep(5);
printf("child process exit, pid=%d, ppid=%d\n", getpid(),getppid());
exit(2);
}
}
else
{
sleep(20);
//5秒后当子进程退出,这个时间用ps ax | grep avoid_zombie 查看,会有一个“Z+”的僵尸进程
//10秒后当孙进程退出,这个时间用ps ax | grep avoid_zombie 查看,
//还是只有一个“Z+”的僵尸进程(子进程),此时孙进程已成为孤儿进程,孙进程由init进程回收,
//孙进程不会成为僵尸进程
//(1)若主进程没有设置signal(SIGCHLD, SIG_IGN),则必须调用wait/waitpid回收子进程.
//(2)若主进程设置了signal(SIGCHLD, SIG_IGN),子进程不会变为僵尸进程,
// 所以主进程无需调用waitpid回收子进程,调用waipid会返回失败。
//参考man waitpid的notes部分:
//if the disposition of SIGCHLD is set to SIG_IGN or the
//SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that termi-
//nate do not become zombies and a call to wait() or waitpid() will block until all
//children have terminated, and then fail with errno set to ECHILD.
int status=0;
do {
ret = waitpid(pid, &status, WUNTRACED | WCONTINUED);
if (ret == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("child[%d] exited, status=%d\n", ret, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
sleep(10);
}
return EXIT_SUCCESS;
}
僵尸进程的避免方法:
方法一:
忽略SIGCLD信号:signal(SIGCHLD, SIG_IGN),系统将不产生僵死进程。
方法二:
两次深度fork(),子进程退出,让孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。