What are orphan processes and zombie processes in PHP7

  • 2021-12-05 05:49:23
  • OfStack

Basic concepts

We know that in unix/linux, under normal circumstances, the child process is created by the parent process, and the child process is creating a new process. The ending of the child process and the running of the parent process are an asynchronous process, that is, the parent process can never predict when the child process will end. When a process completes its work and terminates, its parent process needs to call wait () or waitpid () system calls to obtain the terminated state 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 orphans. Orphan processes will be adopted by the init process (process number 1), and their status will be collected by the init process.

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

Problems and harms

unix provides a mechanism to ensure that if the parent process wants to know the state information of the child process at the end, it can be obtained. This mechanism is that when each process exits, the kernel releases all the resources of the process, including open files, occupied memory and so on. However, a set of information is still reserved for it (including process number the process ID, exit status the termination status of the process, running time the amount of CPU time taken by the process, etc.). It is not released until the parent process fetches it through wait/waitpid. But this leads to problems, If the process does not call wait/waitpid, then the retained information will not be released, its process number will be 1 directly occupied, but the system can use the process number is limited, if a large number of dead processes, because there is no usable process number, resulting in the system can not produce new processes. This is the harm of zombie processes, should be avoided.

Orphan process is a process without a parent process, so the heavy responsibility of orphan process falls on init process, and init process is like a civil affairs bureau, which is specially responsible for dealing with the aftermath of orphan process. Whenever an orphan process occurs, the kernel sets the parent process of the orphan process to init, while the init process loops wait () its exited child process. In this way, when the orphan process ends its life cycle miserably, the init process will deal with its aftermath on behalf of the party and the government. Therefore, the orphan process will not do any harm.

Any one child process (except init) does not disappear immediately after exit (), but leaves a data structure called a zombie process (Zombie) waiting for the parent process to process. This is the stage that each child process goes through at the end. If the child process is after exit () and the parent process has not had time to process it, then the ps command can see that the child process's status is "Z". If the parent process can handle it in time, it may be too late to see the zombie state of the child process with ps command, but this does not mean that the child process does not pass 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 zombie child processes as the parent process.

Zombie process hazard scenario

For example, there is a process, It periodically produces 1 child process, This child process needs to do very little, After doing what it had to do, it quit. Therefore, the life cycle of this child process is very short, but the parent process only generates new child processes, and as for the things after the child processes exit, 1 ignores them. In this way, after the system runs for a period of time, there will be many dead processes in the system. If you use ps command to view, you will see many processes with Z status. Strictly speaking, the dead process is not the root of the problem, the culprit is the parent process that produced a large number of dead processes. Therefore, when we seek to eliminate a large number of dead processes in the system, the answer is to shoot the culprit that 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 init process, and init process will release the resources in the system process table occupied by them. In this way, these dead orphan processes can go in print.

Orphan process and zombie process test

1. The orphan process was adopted by the init process


$pid = pcntl_fork();

if ($pid > 0) {

  //  Displays the process of the parent process ID This function can be getmypid() , you can also use posix_getpid()

  echo "Father PID:" . getmypid() . PHP_EOL;

  //  Stop the parent process for two seconds, during which the parent process of the child process ID Or this parent process 

  sleep(2);

} else if (0 == $pid) {

  //  Let the child process loop 10 Every time, every sleep 1s And then get every second 1 Parent process of the second child process ID

  for ($i = 1; $i <= 10; $i++) {

    sleep(1);

    // posix_getppid() The process () function gets the parent process of the current process ID

    echo posix_getppid() . PHP_EOL;

  }

} else {

  echo "fork error." . PHP_EOL;

}

Test results:


php daemo001.php

Father PID:18046

18046

18046

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ 1

1

1

1

1

1

1

1

2. Zombie process and harm

Execute the following code php zombie1. php


$pid = pcntl_fork();

if( $pid > 0 ){

  //  The following function can be changed php The name of the process 

  cli_set_process_title('php father process');

  //  Let the main process rest 60 Seconds 

  sleep(60);

} else if( 0 == $pid ) {

  cli_set_process_title('php child process');

  //  Let the child process rest 10 Second, but when the process ends, the parent process does not do any processing to the child process, so the child process will become a zombie process 

  sleep(10);

} else {

  exit('fork error.'.PHP_EOL);

}

Execution results, another terminal window


www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18458 0.5 1.2 204068 25920 pts/1  S+  16:34  0:00 php father process

www   18459 0.0 0.3 204068 6656 pts/1  S+  16:34  0:00 php child process

www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18458 0.0 1.2 204068 25920 pts/1  S+  16:34  0:00 php father process

www   18459 0.0 0.0   0   0 pts/1  Z+  16:34  0:00 [php] <defunct>

By executing the ps-aux command, we can see that when the program runs in the first 10 seconds, the state of php child process is listed as [S +], but after 10 seconds, this state becomes [Z +], which means it becomes a zombie process that harms the system.

So, here comes the question? How to avoid zombie process?

PHP helps us solve this problem with two functions, pcntl_wait () and pcntl_waitpid (). Understand Linux system programming should know, look at the name to know that this is actually PHP C language wait () and waitpid () wrapped under 1.

Avoid zombie processes by demonstrating pcntl_wait () in code.

pcntl_wait () function:

This function "waits or returns the status of the child process". When the parent process executes this function, it will block and wait for the status 1 of the child process until the child process has exited or terminated for some reason.

In other words, if the child process is not finished, the parent process will be 1 straight and so on, and if the child process is finished, the parent process will get the child process status immediately. This function returns the process ID of the child process that exits or-1 if it fails.

Execute the following code zombie2.php


$pid = pcntl_fork();

if ($pid > 0) {

  //  The following function can be changed php The name of the process 

  cli_set_process_title('php father process');

  //  Return $wait_result Is the process number of the child process. If the child process is already a zombie process, it is 0

  //  The child process state is saved in the $status Parameter, you can pass the pcntl_wexitstatus() Etc 1 Series functions to view $status What is the status information of 

  $wait_result = pcntl_wait($status);

  print_r($wait_result);

  print_r($status);

  //  Let the main process rest 60 Seconds 

  sleep(60);

} else if (0 == $pid) {

  cli_set_process_title('php child process');

  //  Let the child process rest 10 Second, but when the process ends, the parent process does not do any processing to the child process, so the child process will become a zombie process 

  sleep(10);

} else {

  exit('fork error.' . PHP_EOL);

}

Looking through ps-aux in another terminal, we can see that php child process is in [S +] state in the first 10 seconds, and then the process disappears after 10 seconds, that is, it is recycled by the parent process and has not become a zombie process.


www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18519 0.5 1.2 204068 25576 pts/1  S+  16:42  0:00 php father process

www   18520 0.0 0.3 204068 6652 pts/1  S+  16:42  0:00 php child process

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18519 0.0 1.2 204068 25576 pts/1  S+  16:42  0:00 php father process

However, there is a big problem with pcntl_wait (), which is blocking. The parent process can only hang and wait for the child process to end or terminate, while the parent process can do nothing, which is not in line with the principle of fast, good and economical, so pcntl_waitpid () comes on stage. pcntl_waitpid (pid, & If the third parameter of status, $option = 0) is set to WNOHANG, the parent process will not block 1 until a child process exits or terminates, otherwise it will behave like pcntl_wait ().

Modify the code for Case 3, but instead of adding WNOHANG, we demonstrate the pcntl_waitpid () functionality:


$pid = pcntl_fork();

if ($pid > 0) {

  //  The following function can be changed php The name of the process 

  cli_set_process_title('php father process');

  //  The return value is saved in the $wait_result Medium 

  // $pid Parameter representation   Processes of child processes ID

  //  The child process state is saved in the parameter $status Medium 

  //  Will be 3 A option Parameter to a constant WNOHANG You can prevent the main process from blocking and suspending, where the parent process immediately returns to continue executing the rest of the code 

  $wait_result = pcntl_waitpid($pid, $status);

  var_dump($wait_result);

  var_dump($status);

  //  Let the main process rest 60 Seconds 

  sleep(60);

} else if (0 == $pid) {

  cli_set_process_title('php child process');

  //  Let the child process rest 10 Second, but when the process ends, the parent process does not do any processing to the child process, so the child process will become a zombie process 

  sleep(10);

} else {

  exit('fork error.' . PHP_EOL);

}

Here is the result, a terminal window that executes the php zombie3. php program


www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ php zombie3.php

int(18586)

int(0)

^C    

ctrl-c sends an SIGINT signal to all processes in the foreground process group. Commonly used to terminate a running program.

The following is the ps-aux terminal window


www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18605 0.3 1.2 204068 25756 pts/1  S+  16:52  0:00 php father process

www   18606 0.0 0.3 204068 6636 pts/1  S+  16:52  0:00 php child process

www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18605 0.1 1.2 204068 25756 pts/1  S+  16:52  0:00 php father process

www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php

www   18605 0.0 1.2 204068 25756 pts/1  S+  16:52  0:00 php father process

www@iZ2zec3dge6rwz2uw4tveuZ:~$ ps -aux|grep -v "grep\|nginx\|php-fpm" | grep php // ctrl-c  It is no longer blocked after 

www@iZ2zec3dge6rwz2uw4tveuZ:~$

You can actually see that the main process is blocked, 1 until the child process exits and the parent process is no longer blocked in the 10th second

Modify the code in paragraph 4 and add the third parameter WNOHANG, which reads as follows:


$pid = pcntl_fork();

if ($pid > 0) {

  //  The following function can be changed php The name of the process 

  cli_set_process_title('php father process');

  //  The return value is saved in the $wait_result Medium 

  // $pid Parameter representation   Processes of child processes ID

  //  The child process state is saved in the parameter $status Medium 

  //  Will be 3 A option Parameter to a constant WNOHANG You can prevent the main process from blocking and suspending, where the parent process immediately returns to continue executing the rest of the code 

  $wait_result = pcntl_waitpid($pid, $status, WNOHANG);

  var_dump($wait_result);

  var_dump($status);

  echo " Not blocking, run here " . PHP_EOL;

  //  Let the main process rest 60 Seconds 

  sleep(60);

} else if (0 == $pid) {

  cli_set_process_title('php child process');

  //  Let the child process rest 10 Second, but when the process ends, the parent process does not do any processing to the child process, so the child process will become a zombie process 

  sleep(10);

} else {

  exit('fork error.' . PHP_EOL);

}

Execute php zombie4. php


php daemo001.php

Father PID:18046

18046

18046

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ 1

1

1

1

1

1

1

1
0

Another ps-aux terminal window


php daemo001.php

Father PID:18046

18046

18046

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ 1

1

1

1

1

1

1

1
1

You can actually see that the main process is blocked, 1 until the child process exits and the parent process is no longer blocked in the 10th second.   

The problem arises, and the process state of php child process actually becomes [Z +]. How did this happen? Go back and analyze the code under 1:
We can see that the child process slept for 10 seconds, while the parent process did not sleep and was no longer blocked before executing pcntl_waitpid (). Therefore, the main process executed itself first, while the child process ended after 10 seconds, and the process state could not be recovered naturally.

If we modify the code by 1, we will sleep for 15 seconds before the main process's pcntl_waitpid (), so that we can recycle the child process. However, even if you modify it like this, there will still be a problem if you think about it carefully, that is, after the child process ends and before the parent process executes pcntl_waitpid () recycling, there will be a time difference of 5 seconds. In this time difference, php child process will also be a zombie process. So, how can pcntl_waitpid () be used correctly? After all, it doesn't seem very scientific to use it like this.

Then, it's time to introduce signaling!


Related articles: