PHP的多进程--防止僵尸进程

何为僵尸进程

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源

僵尸进程带来的问题

僵尸进程由于父进程不回收系统保留的信息而一直占用着系统资源,其中有一项叫做进程描述符。系统通过分配它来启动一个进程。
但是系统所能使用的进程号是有限的,如果存在大量的僵尸进程,系统将因为没有可用的进程号而导致系统不能产生新的进程

如何查看僵尸进程

僵尸进程在系统中用 <defunct> 或 <z> 表示,通过 ps -ef 指令查看进程,如果发现某个进程的状态为 <defunct>/<z>,说明该进程是一个僵尸进程。

$ ps -ef | grep php
root      19355  17941  0 13:53 pts/3    00:00:00 php fork.php
root      19356  19355  0 13:53 pts/3    00:00:00 [php] <defunct>
root      19392  18316  0 13:53 pts/4    00:00:00 grep --color php

如何解决

  • 通过WAIT/WAITPID系统,等待子进程结束,并回收。

php代码实现

<?php
$pid = pcntl_fork();

if($pid == -1) {
    die('fork error');
}elseif($pid) {
    // 父进程阻塞着等待子进程的退出
    pcntl_wait($status);
    pcntl_waitpid($pid, $status);

    // 非阻塞方式,通过WNOHANG区分
    //pcntl_wait($status, WNOHANG);
    //pcntl_waitpid($pid, $status, WNOHANG);
    // 如没有上面的函数,在父进程sleep的时候,子进程就是僵尸进程
    sleep(20);
}else{
    cli_set_process_title("childTest");
    echo "child \r\n";
    exit;
}

/**
 *  int pcntl_wait ( int &$status [, int $options ] ):阻塞当前进程,直到当前进程的一个子进程退出或者收到一个结束当前进程的信号。
 *  int pcntl_waitpid ( int $pid , int &$status [, int $options ] ):功能同 pcntl_wait,区别为 waitpid 为等待指定 pid 的子进程。当 pid 为 -1
 * 时 pcntl_waitpid 与 pcntl_wait 一样。
 */
  • 通过SIGCHLD信号

当子进程结束后,系统会像父进程发送 SIGCHLD 信号给父进程,通知父进程子进程已经结束,但父进程默认不处理。我们可以在父进程收到这个信号时调用 wait()/waitpid() 来回收。

<?php
// 每执行一次低级语句会检查一次该进程是否有未处理过的信号
declare(ticks = 1);

// 信号处理函数
function sig_func() {
    echo "SIGCHLD \r\n";

    // 阻塞
    pcntl_wait($status);
    //pcntl_waitpid(-1, $status);

    // 非阻塞
    //pcntl_wait($status, WNOHANG);
    //pcntl_waitpid(-1, $status, WNOHANG);
}

pcntl_signal(SIGCHLD, 'sig_func');

$pid = pcntl_fork();

if($pid == -1) {
    die('fork error');
}elseif($pid) {
    // 父进程一直执行
    while(1) {
        sleep(5);
    }
}else{
    echo "child \r\n";
    exit;
}
  • 通过操作系统信号

如果父进程不关心子进程什么时候结束,那么可以用 pcntl_signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。

<?php
// 每执行一次低级语句会检查一次该进程是否有未处理过的信号
declare(ticks = 1);

pcntl_signal(SIGCHLD, SIG_IGN);

$pid = pcntl_fork();

if($pid == -1) {
    die('fork error');
}elseif($pid) {
    // 父进程一直执行
    while(1) {
        sleep(5);
    }
}else{
    echo "child \r\n";
    exit;
}

参考文章:http://www.cnblogs.com/loveyouyou616/p/8881531.html

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 我们知道在unix/linux中,子进程的结束和父进程的运行是一个异步过程, 当一个进程完成它的工作终止之后,它的...
    丶Em1tu0F阅读 4,394评论 0 3
  • 多进程--fork 场景:日常任务中,有时需要通过php脚本执行一些日志分析,队列处理等任务,当数据量比较大时,可...
    金星show阅读 4,788评论 0 2
  • 1.内存的页面置换算法 (1)最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的...
    杰伦哎呦哎呦阅读 8,650评论 1 9
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 9,606评论 0 23
  • 情绪,的确解决不了任何问题,时间在继续,没有任何人或事会为你停下来。 前段日子,为了保持自己精神亢奋,咖啡一杯接一...
    葡萄在上阅读 3,056评论 1 2