Почему существуют зомби-процессы?

Википедия говорит: "Ребенок-процесс, который заканчивается, но его никогда не ждет, его родитель становится процессом зомби". Я запустил эту программу:

#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
....
.....

Но почему в этом случае "дочерний процесс завершается, но его не ждет его родитель"?

Ответ 1

В вашем коде зомби создается на exit(0) (комментарий со стрелкой ниже):

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

Почему? Потому что на нем никогда не было wait. Когда что-то вызывает waitpid(pid), он возвращает посмертную информацию о процессе, например, код выхода. К сожалению, когда процесс завершен, ядро ​​не может просто избавиться от этой записи процесса, иначе код возврата будет потерян. Таким образом, он ждет кого-то на wait на нем и оставляет эту запись процесса даже в том случае, если она действительно не занимает никакой памяти, кроме записи в таблице процессов - это именно то, что называется зомби.

У вас мало возможностей избежать создания зомби:

  • Добавить waitpid() в родительский процесс. Например, это поможет:

    pid=fork();
    if (pid==0) {
        exit(0);    
    } else {
        waitpid(pid);  // <--- this call reaps zombie
        // some parent code ...
    }
    
  • Выполните двойную fork(), чтобы получить внука и выйти из ребенка, пока внук все еще жив. Внуки будут автоматически приняты init, если их родитель (наш ребенок) умрет, а это означает, что если внук умрет, он будет автоматически wait ed на init. Другими словами, вам нужно сделать что-то вроде этого:

    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 ...
    }
    
  • Явно игнорировать сигнал 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 ...
    }