PHP 多进程之孤儿和僵尸简单讲解

PHP多进程之孤儿/僵尸

what

init进程

普通用户运行但有超级权限的进程

孤儿进程

父进程在子进程处理完前退出, 子进程就会变成孤儿进程;并由init进程进行处理

僵尸进程

  • 「z+」标记
  • 父进程fork子进程后, 没有调用wait来维护子进程,导致子进程无人管理

why

总是不断fork, 不管维护。系统资源有限 -- 就会出现僵尸进程

how

相比孤儿进程,僵尸进程危害更大,毕竟孤儿还有init妈妈来抚养,僵尸就只能独自游荡!

pcntl_wait
pcntl_waitpid

等待或返回fork的子进程状态

相同
pcntl_wait($status, $option) == pcntl_waitpid(-1, $status, $option)
pcntl_waitpid($pid, $status, $option)

pid

<-1: 等待任意进程组ID等于参数pid给定值的绝对值的进程
-1: 等待任意子进程;与pcntl_wait函数行为一致
0: 等待任意与调用进程组ID相同的子进程

0: 等待进程号等于参数pid值的子进程

option

WNOHANG: 如果没有子进程退出立刻返回
WUNTRACED: 子进程已经退出并且其状态未报告时返回

孤儿进程

<?php

$pid = pcntl_fork();

switch($pid) {

        case 0:
        // 子进程
        echo "I am son  parent's pid=".posix_getppid()."\n";
        for( $i = 1; $i <= 10; $i++ ){
                sleep( 1 );
                // posix_getppid()函数的作用就是获取当前进程的父进程进程ID
                echo "parent's pid =" posix_getppid().PHP_EOL;
        }
        break;

        case -1:
        // fork error
        echo "I am err\n";
        break;

        default:
        // 父进程
        echo "I am parent pid=".posix_getpid()."\n";
        sleep(2);
        break;
}
// 第三秒后 由init进程收养

僵尸进程

<?php
$pid = pcntl_fork();
switch($pid) {
        case 0:
        // 子进程
        cli_set_process_title("son process php");
        sleep(5);
        break;

        case -1:
        // fork error
        break;

        default:
        // 父进程
        cli_set_process_title("parent process php");
        sleep(30);
        break;
}
// 子进程执行后父进程未取管理导致

解决

<?php
switch($pid) {
        case 0:
        // 子进程
        cli_set_process_title("son process php");
        sleep(10);
        break;

        case -1:
        // fork error
        break;

        default:
        // 父进程
        cli_set_process_title("parent process php");
        $iid = pcntl_wait($status); // 等待子进程的状态处理

        // 监控子进程状态处理后才能输出
        // 只要未触发pcntl_wait,此后的逻辑一直不会处理
        echo $iid."-----\n";

        //sleep(60);
        break;
}

===
// 增加pcntl_wait
// pcntl_wait($status); // 只要子进程不退出, 父进程就会阻塞再这个地方

更优解决

<?php
$pid = pcntl_fork();

switch($pid) {
        case 0:
        // 子进程
        cli_set_process_title("son process php");
        sleep(10);
        break;

        case -1:
        // fork error
        break;

        default:
        // 父进程
        cli_set_process_title("parent process php");
        pcntl_waitpid($pid, $status, WNOHANG);      // 监控$pid, 如果没有子进程退出立刻返回
        echo "I am parent\n";
        //sleep(60);
        break;
}

===
// pcntl_wait改写pcntl_waitpid
// pcntl_waitpid($pid, $status, WNOHANG); 
// 如果再pcntl_waitpid后续增加sleep, 还是会僵尸进程

更更优解决

信号

本作品采用《CC 协议》,转载必须注明作者和本文链接
taozywu
讨论数量: 4
taozywu

感谢各位阅览,下节分享信号!

4年前 评论

第一个例子

echo "I am son pid=".posix_getppid()."\n";

应该是

echo "I am son pid=".posix_getpid()."\n";

才取到子进程的进程id吧

4年前 评论
taozywu (楼主) 4年前
taozywu
4年前 评论

对于僵尸进程解决的第一个例子,父进程应该阻塞在default子句里面的pcntl_wait处,而不是外面的pcntl_wait处;对于“更优解决方案”,父进程先于子进程退出,子进程变成了孤儿进程,所以不算“更优解决方案”吧?

4年前 评论
taozywu (楼主) 4年前
taozywu (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!