为什么存在僵尸进程

为什么存在僵尸进程

本文介绍了为什么存在僵尸进程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

维基百科说:一个终止但从未被其父进程等待的子进程变成了僵尸进程."我运行此程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t pid, ppid;
    printf("Hello World1\n");
    pid=fork();
    if(pid==0)
    {
        exit(0);
    }
    else
    {
        while(1)
        {
        printf("I am the parent\n");
        printf("The PID of parent is %d\n",getpid());
        printf("The PID of parent of parent is %d\n",getppid());
        sleep(2);
        }
    }
}

这会创建一个僵尸进程,但我不明白为什么在这里创建了僵尸进程?

程序的输出是

Hello World1
I am the parent
The PID of parent is 3267
The PID of parent of parent is 2456
I am the parent
The PID of parent is 3267
The PID of parent of parent is 2456
I am the parent
....
.....

但是为什么在这种情况下子进程终止但没有被其父进程等待"呢?

解决方案

在您的代码中,僵尸是在exit(0)上创建的(带有以下箭头的注释):

pid=fork();
if (pid==0) {
    exit(0);  // <--- zombie is created on here
} else {
    // some parent code ...
}

为什么?因为您从没有wait上过它.当某人调用waitpid(pid)时,它将返回有关进程的事后信息,例如其退出代码.不幸的是,当进程退出时,内核不能仅仅处理该进程条目,否则返回代码将丢失.因此,它等待某人对它进行wait操作,并保留该进程条目,即使除了进程表中的条目之外,它实际上并没有占用任何内存-这就是所谓的 zombie . /p>

您有几种选择来避免创建僵尸:

  1. 在父进程的某处添加waitpid() .例如,这样做会有所帮助:

    pid=fork();
    if (pid==0) {
        exit(0);
    } else {
        waitpid(pid);  // <--- this call reaps zombie
        // some parent code ...
    }
    

  2. 执行两次fork() 获取孙子并在孙子还活着的情况下在孩子中退出.如果其父母(我们的孩子)去世,则孙子将由init自动收养,这意味着如果孙子去世,则其将由init自动wait.换句话说,您需要执行以下操作:

    pid=fork();
    if (pid==0) {
        // child
        if (fork()==0) {
            // grandchild
            sleep(1); // sleep a bit to let child die first
            exit(0);  // grandchild exits, no zombie (adopted by init)
        }
        exit(0);      // child dies first
    } else {
         waitpid(pid);  // still need to wait on child to avoid it zombified
         // some parent code ...
    }
    

  3. 明确忽略父级中的SIGCHLD信号.当孩子去世时,父母会收到SIGCHLD信号,让其对孩子的死亡做出反应.您可以在收到此信号后调用waitpid(),或者可以安装显式的忽略信号处理程序(使用signal()sigaction()),这将确保子代不会成为僵尸.换句话说,是这样的:

    signal(SIGCHLD, SIG_IGN); // <-- ignore child fate, don't let it become zombie
    pid=fork();
    if (pid==0) {
        exit(0); // <--- zombie should NOT be created here
    } else {
         // some parent code ...
    }
    

Wikipedia says "A child process that terminates but is never waited on by its parent becomes a zombie process." I run this program:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t pid, ppid;
    printf("Hello World1\n");
    pid=fork();
    if(pid==0)
    {
        exit(0);
    }
    else
    {
        while(1)
        {
        printf("I am the parent\n");
        printf("The PID of parent is %d\n",getpid());
        printf("The PID of parent of parent is %d\n",getppid());
        sleep(2);
        }
    }
}

This creates a zombie process, but I can't understand why a zombie process is created here?

The output of the program is

Hello World1
I am the parent
The PID of parent is 3267
The PID of parent of parent is 2456
I am the parent
The PID of parent is 3267
The PID of parent of parent is 2456
I am the parent
....
.....

But why is it that the "child process terminates but is not waited on by its parent" in this case?

解决方案

In your code, zombie is created on exit(0) (comment with arrow below):

pid=fork();
if (pid==0) {
    exit(0);  // <--- zombie is created on here
} else {
    // some parent code ...
}

Why? Because you never waited on it. When something calls waitpid(pid), it returns postmortem information about process, like its exit code. Unfortunately, when process exited, kernel cannot just dispose of this process entry, or return code will be lost. So it waits for somebody to wait on it, and leaves this process entry around even if it does not really occupy any memory except for entry in process table - this is exactly what is called zombie.

You have few options to avoid creating zombies:

  1. Add waitpid() somewhere in the parent process. For example, doing this will help:

    pid=fork();
    if (pid==0) {
        exit(0);
    } else {
        waitpid(pid);  // <--- this call reaps zombie
        // some parent code ...
    }
    

  2. Perform double fork() to obtain grandchild and exit in child while grandchild is still alive. Grandchildren will be automatically adopted by init if their parent (our child) dies, which means if grandchild dies, it will be automatically waited on by init. In other words, you need to do something like this:

    pid=fork();
    if (pid==0) {
        // child
        if (fork()==0) {
            // grandchild
            sleep(1); // sleep a bit to let child die first
            exit(0);  // grandchild exits, no zombie (adopted by init)
        }
        exit(0);      // child dies first
    } else {
         waitpid(pid);  // still need to wait on child to avoid it zombified
         // some parent code ...
    }
    

  3. Explicitly ignore SIGCHLD signal in parent. When child dies, parent gets sent SIGCHLD signal which lets it react on children death. You can call waitpid() upon receiving this signal, or you can install explicit ignore signal handler (using signal() or sigaction()), which will make sure that child does not become zombie. In other words, something like this:

    signal(SIGCHLD, SIG_IGN); // <-- ignore child fate, don't let it become zombie
    pid=fork();
    if (pid==0) {
        exit(0); // <--- zombie should NOT be created here
    } else {
         // some parent code ...
    }
    

这篇关于为什么存在僵尸进程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 05:34