原文地址:Zombie Processes and their Prevention

僵尸态:在UNIX中使用 fork() 系统调用创建进程时,将复制父进程的地址空间。如果父进程调用 wait() 系统调用,那么将暂停父进程的执行,直到子进程终止。在子进程终止时,会生成一个 “SIGCHLD” 信号,该信号会由内核传递给父进程。父进程在收到 “SIGCHLD” 后会从进程表中获得子进程的状态。即使子进程终止了,在进程表中也有一个对应于子进程的条目,该条目存储了状态。当父级收集状态时,该条目将被删除。因此,将从系统中删除子进程的所有痕迹。如果父进程决定不等待子进程终止而执行后续任务,则在子进程终止时,父进程不会读取退出状态。因此,即使在终止子进程之后,进程表中仍会保留一个条目。子进程的这种状态称为僵尸态。

// A C program to demonstrate working of
// fork() and process table entries.
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
int i;
int pid = fork();

if (pid == 0)
{
for (i=0; i<20; i++)
printf("I am Child\n");
}
else
{
printf("I am Parent\n");
while(1);
}
}

输出:

现在,在终端中使用以下命令检查进程表

$ ps -eaf

此处 [a.out] 失效的条目显示了僵尸进程。

为什么我们需要阻止僵尸进程产生?

每个系统只有一个进程表。进程表的大小是有限的。如果生成太多僵尸进程,则进程表将会满。也就是说,系统将无法生成任何新进程,然后系统将陷入停顿。因此,我们需要防止创建僵尸进程。

可以防止创建僵尸的不同方式

使用 wait() 系统调用

父进程在创建子进程之后调用 wait() 时,它将等待子进程完成,并获得子进程的退出状态。父进程被挂起(在等待队列中等待),直到子进程终止。必须理解,在此期间,父进程只是等待而已。

// A C program to demonstrate working of 
// fork()/wait() and Zombie processes
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
int i;
int pid = fork();
if (pid==0)
{
for (i=0; i<20; i++)
printf("I am Child\n");
}
else
{
wait(NULL);
printf("I am Parent\n");
while(1);
}
}

通过忽略 SIGCHLD 信号

子进程终止时,相应的 SIGCHLD 信号会传递给父进程,如果我们调用 signal(SIGCHLD,SIG_IGN),则系统会忽略 SIGCHLD 信号,并从进程表中删除子进程条目。因此,不会创建僵尸进程。但是,在这种情况下,父进程无法知道子进程的退出状态。

// A C program to demonstrate ignoring 
// SIGCHLD signal to prevent Zombie processes
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
int i;
int pid = fork();
if (pid == 0)
for (i=0; i<20; i++)
printf("I am Child\n");
else
{
signal(SIGCHLD, SIG_IGN);
printf("I am Parent\n");
while(1);
}
}

通过使用信号处理程序

父进程为 SIGCHLD 信号设置信号处理程序。信号处理程序在其中调用 wait() 系统调用。在这种情况下,当子进程终止时,SIGCHLD 将传递给父进程。收到 SIGCHLD 时,将激活相应的处理程序,该处理程序将依次调用 wait() 系统调用。因此,父进程几乎立即收集了退出状态,并且清除了进程表中的子进程条目。因此,不会创建僵尸进程。

// A C program to demonstrate handling of 
// SIGCHLD signal to prevent Zombie processes.
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

void func(int signum)
{
wait(NULL);
}

int main()
{
int i;
int pid = fork();
if (pid == 0)
for (i=0; i<20; i++)
printf("I am Child\n");
else
{
signal(SIGCHLD, func);
printf("I am Parent\n");
while(1);
}
}

输出:

这里没有任何[a.out]失效,即没有创建任何僵尸进程。