Detailed explanation of Linux orphan process and zombie process

1. Foreword

When looking at Chapter 8 of advanced programming in unix environment, I mentioned orphan process and zombie process. I have been vague about these two concepts. Today, I was asked what is the orphan process and zombie process, what problems will be brought about and how to solve them. I just stay on the concept without going deep. I feel ashamed. In the evening, I came back to google, referred to APUE again, carefully summarized and deepened my understanding.

2. Basic concepts

We know that in unix/linux, under normal circumstances, the child process is created through the parent process, and the child process is creating a new process. The end of the child process and the operation of the parent process are asynchronous processes, that is, the parent process can never predict when the child process will end. When a process finishes its work and terminates, its parent process needs to call wait() or waitpid() system call to obtain the termination status of the child process.

Orphan process: if a parent process exits and one or more of its child processes are still running, those child processes will become orphan processes. Orphan processes will be adopted by the init process (process number 1), and the init process will complete the status collection for them.

Zombie process: a process uses fork to create a child process. If the child process exits and the parent process does not call wait or waitpid to obtain the status information of the child process, the process descriptor of the child process is still saved in the system. This process is called a dead end process.

3. Problems and hazards

unix provides a mechanism to ensure that as long as the parent process wants to know the state information of the child process at the end, it can get it. This mechanism is: when each process exits, the kernel releases all the resources of the process, including open files, occupied memory, etc. However, certain information is still reserved for it (including the process number, the process ID, the termination status of the process, the amount of CPU time taken by the process, etc.). It is not released until the parent process fetches it through wait / waitpid. However, this leads to problems. If the process does not call wait / waitpid, the retained information will not be released and its process number will be occupied all the time. However, the process number that the system can use is limited. If a large number of dead processes are generated, the system will not be able to generate new processes because there are no available process numbers This is the harm of zombie process and should be avoided.

The orphan process is a process without a parent process. The important task of the orphan process falls to the init process. The init process is like a Civil Affairs Bureau, which is specially responsible for dealing with the aftermath of the orphan process. Whenever an orphan process appears, the kernel sets the parent process of the orphan process to init, and the init process will wait() its exited child process cyclically. In this way, when an orphan process sadly ends its life cycle, init process will deal with all its aftermath on behalf of the party and the government. Therefore, the orphan process will not do any harm.

Any child process (except init) does not disappear immediately after exit(), but leaves a data structure called zombie process to be processed by the parent process. This is the stage that each child process goes through at the end. If the parent process does not have time to process the child process after exit(), you can see that the state of the child process is "Z" with the ps command. If the parent process can handle it in time, it may be too late to see the zombie state of the child process with the ps command, but this does not mean that the child process does not go through the zombie state. If the parent process exits before the child process ends, the child process will be taken over by init. Init will process the child process in zombie state as the parent process.

Zombie process hazard scenario:

For example, there is a process that periodically generates a child process. The child process needs to do very little. After it completes what it should do, it exits. Therefore, the life cycle of the child process is very short. However, the parent process only generates new child processes, and ignores everything after the child process exits. In this way, After the system runs for a period of time, there will be many dead processes in the system. If you use the ps command to view, you will see many processes in Z status. Strictly speaking, the dead process is not the root of the problem. The culprit is the parent process that produces a large number of dead processes. Therefore, when we seek how to eliminate a large number of dead processes in the system, the answer is to shoot the culprit who produced a large number of dead processes (that is, send SIGTERM or SIGKILL signals through kill). After shooting the culprit process, the dead process it produces becomes an orphan process. These orphan processes will be taken over by the init process, and the init process will wait() these orphan processes to release the resources in the system process table they occupy. In this way, these dead orphan processes can go in peace.

3. Orphan process and zombie process testing

The orphan process test procedure is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

int main(int argc,char** argv)
{
    pid_t pid;
    //Create a process
    pid = fork();
    //Creation failed
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    //Subprocess
    if (pid == 0)
    {
        printf("I am the child process.\n");
        //Output process ID and parent process ID
        printf("pid: %d\tppid:%d\n",getpid(),getppid());
        printf("I will sleep five seconds.\n");
        //Sleep for 5s to ensure that the parent process exits first
        sleep(5);
        printf("pid: %d\tppid:%d\n",getpid(),getppid());
        printf("child process is exited.\n");
    }
    //Parent process
    else
    {
        printf("I am father process.\n");
        //The parent process sleeps for 1s to ensure that the child process outputs the process id
        sleep(1);
        printf("father process is  exited.\n");
    }
    return 0;
}

The test results are as follows:

The zombie process test procedure is as follows:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am child process.I am exiting.\n");
        exit(0);
    }
    printf("I am father process.I will sleep two seconds\n");
    //Wait for the child process to exit first
    sleep(2);
    //Output process information
    system("ps -o pid,ppid,state,tty,command");
    printf("father process is exiting.\n");
    return 0;
}

The test results are as follows:

Zombie process test 2: the parent process creates child processes circularly, and the child processes exit, resulting in multiple zombie processes. The program is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    pid_t  pid;
    //Loop create child process
    while(1)
    {
        pid = fork();
        if (pid < 0)
        {
            perror("fork error:");
            exit(1);
        }
        else if (pid == 0)
        {
            printf("I am a child process.\nI am exiting.\n");
            //The child process exits and becomes a zombie process
            exit(0);
        }
        else
        {
            //The parent process hibernates for 20s and continues to create child processes
            sleep(20);
            continue;
        }
    }
    return 0;
}

The program test results are as follows:

4. Zombie process solution

(1) Through signaling mechanism

When the child process exits, it sends the SIGCHILD signal to the parent process, and the parent process processes the SIGCHILD signal. In the signal processing function, wait is called to process the zombie process. The test procedure is as follows:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>

static void sig_child(int signo);

int main()
{
    pid_t pid;
    //Create capture subprocess exit signal
    signal(SIGCHLD,sig_child);
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am child process,pid id %d.I am exiting.\n",getpid());
        exit(0);
    }
    printf("I am father process.I will sleep two seconds\n");
    //Wait for the child process to exit first
    sleep(2);
    //Output process information
    system("ps -o pid,ppid,state,tty,command");
    printf("father process is exiting.\n");
    return 0;
}

static void sig_child(int signo)
{
     pid_t        pid;
     int        stat;
     //Dealing with zombie processes
     while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
            printf("child %d terminated.\n", pid);
}

The test results are as follows:

(2) fork twice
Section 8.6 of advanced programming in Unix environment is very detailed. The principle is to turn the child process into an orphan process, so that its parent process becomes an init process, through which the zombie process can be handled. The test procedure is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    pid_t  pid;
    //Create the first child process
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    //First child process
    else if (pid == 0)
    {
        //Subprocess re creation subprocess
        printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
        pid = fork();
        if (pid < 0)
        {
            perror("fork error:");
            exit(1);
        }
        //The first child process exits
        else if (pid >0)
        {
            printf("first procee is exited.\n");
            exit(0);
        }
        //Second sub process
        //Sleep 3s ensures that the first child process exits, so that the father of the second child process is in the init process
        sleep(3);
        printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
        exit(0);
    }
    //The parent process handles the exit of the first child process
    if (waitpid(pid, NULL, 0) != pid)
    {
        perror("waitepid error:");
        exit(1);
    }
    exit(0);
    return 0;
}

The test results are shown in the figure below:

Keywords: Linux

Added by bond00 on Sun, 20 Feb 2022 20:14:24 +0200