孤儿进程与僵尸进程产生及其处理

有编程经验的同学多少都听说过孤儿进程和僵尸进程,这其实是两个不一样的概念

孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程,在Linux系统中,我们可以通过ps aux来查看进程,如果有[Z+]标记就是僵尸进程

在PHP中,父进程对子进程的状态收集等是通过pcntl_wait()和pcntl_waitpid()等完成的

演示并说明孤儿进程的出现,并演示孤儿进程被init进程收养:

<?php

$pid = pcntl_fork();
if( $pid > 0 ){
  // 显示父进程的进程ID,这个函数可以是getmypid(),也可以用posix_getpid()
  echo "Father PID:".getmypid().PHP_EOL;
  // 让父进程停止两秒钟,在这两秒内,子进程的父进程ID还是这个父进程
  sleep( 2 );
} else if( 0 == $pid ) {
  // 让子进程循环10次,每次睡眠1s,然后每秒钟获取一次子进程的父进程进程ID
  for( $i = 1; $i <= 10; $i++ ){
    sleep( 1 );
    // posix_getppid()函数的作用就是获取当前进程的父进程进程ID
    echo posix_getppid().PHP_EOL;
  }
} else {
  echo "fork error.".PHP_EOL;
}

运行结果如下:

[viktor@kali scripts]# php demo.php
Father PID:27416
27416
27416
[viktor@kali scripts]# 1
1
1
1
1
1
1
1

可以看到,前两秒内,子进程的父进程进程ID为4129,但是从第三秒开始,由于父进程已经提前退出了,子进程变成孤儿进程,所以init进程收养了子进程,所以子进程的父进程进程ID变成了1。

演示并说明僵尸进程的出现,并演示僵尸进程的危害:

<?php

$pid = pcntl_fork();
if( $pid > 0 ){
  // 下面这个函数可以更改php进程的名称
  cli_set_process_title('php father process');
  // 让主进程休息60秒钟
  sleep(60);
} else if( 0 == $pid ) {
  cli_set_process_title('php child process');
  // 让子进程休息10秒钟,但是进程结束后,父进程不对子进程做任何处理工作,这样这个子进程就会变成僵尸进程
  sleep(10);
} else {
  exit('fork error.'.PHP_EOL);
}

运行结果如下:

[viktor@kali scripts]# ps axu | grep php
root     27528  0.1  0.8 293352 14296 pts/0    S+   11:38   0:00 php father process
root     27529  0.0  0.3 293352  6572 pts/0    S+   11:38   0:00 php child process
root     27531  0.0  0.0 112832   988 pts/1    S+   11:38   0:00 grep --color=auto php
[viktor@kali scripts]# ps axu | grep php
root     27528  0.0  0.8 293352 14296 pts/0    S+   11:38   0:00 php father process
root     27529  0.0  0.0      0     0 pts/0    Z+   11:38   0:00 [php] <defunct>
root     27535  0.0  0.0 112832   984 pts/1    S+   11:38   0:00 grep --color=auto php

通过执行ps aux命令可以看到,当程序在前十秒内运行的时候,php child process的状态列为[S+],然而在十秒钟过后,这个状态变成了[Z+],也就是变成了危害系统的僵尸进程。

那么,问题来了?如何避免僵尸进程呢?PHP通过pcntl_wait()和pcntl_waitpid()两个函数来帮我们解决这个问题。了解Linux系统编程的应该知道,看名字就知道这其实就是PHP把C语言中的wait()和waitpid()包装了一下。

通过代码演示pcntl_wait()来避免僵尸进程,在开始之前先简单普及一下pcntl_wait()的相关内容:这个函数的作用就是 “ 等待或者返回子进程的状态 ”,当父进程执行了该函数后,就会阻塞挂起等待子进程的状态一直等到子进程已经由于某种原因退出或者终止。换句话说就是如果子进程还没结束,那么父进程就会一直等等等,如果子进程已经结束,那么父进程就会立刻得到子进程状态。这个函数返回退出的子进程的进程ID或者失败返回-1。

我们将第二个案例中代码修改一下:
<?php

$pid = pcntl_fork();
if( $pid > 0 ){
  // 下面这个函数可以更改php进程的名称
  cli_set_process_title('php father process');

  // 返回$wait_result,就是子进程的进程号,如果子进程已经是僵尸进程则为0
  // 子进程状态则保存在了$status参数中,可以通过pcntl_wexitstatus()等一系列函数来查看$status的状态信息是什么
  $wait_result = pcntl_wait( $status );
  print_r( $wait_result );
  print_r( $status );

  // 让主进程休息60秒钟
  sleep(60);
} else if( 0 == $pid ) {
  cli_set_process_title('php child process');
  // 让子进程休息10秒钟,但是进程结束后,父进程不对子进程做任何处理工作,这样这个子进程就会变成僵尸进程
  sleep(10);
} else {
  exit('fork error.'.PHP_EOL);
}

执行结果:

[viktor@kali scripts]# ps axu | grep php
root     27528  0.1  0.8 293352 14296 pts/0    S+   11:38   0:00 php father process
root     27529  0.0  0.3 293352  6572 pts/0    S+   11:38   0:00 php child process
root     27531  0.0  0.0 112832   988 pts/1    S+   11:38   0:00 grep --color=auto php
[viktor@kali scripts]# ps axu | grep php
root     27528  0.0  0.8 293352 14296 pts/0    S+   11:38   0:00 php father process
root     27535  0.0  0.0 112832   984 pts/1    S+   11:38   0:00 grep --color=auto php

在另外一个终端中通过ps aux查看,可以看到在前十秒内,php child process是[S+]状态,然后十秒钟过后进程消失了,也就是被父进程回收了,没有变成僵尸进程。

好了,本篇文章探讨了孤儿进程和僵尸进程的产生和处理方法;关于僵尸进程看似已经处理好了,其实还有一些其他问题,等以后的文章再讨论吧

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇