原文地址:http://twei.site/2017/08/08/PHP%E7%9A%84%E5%A4%9A%E8%BF%9B%E7%A8%8B-%E9%98%B2%E6%AD%A2%E5%83%B5%E5%B0%B8%E8%BF%9B%E7%A8%8B/

正文

多进程编码中,一个不得不注意的问题就是僵尸进程(zombie process)。在 PHP 的多进程编码中,也是如此。

什么是僵尸进程

僵尸进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait() 或 waitpid() 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这类子进程被称为僵尸进程。

我们详细理解下,在 UNIX/Linux 中,正常情况下,子进程是通过 fork 父进程创建的。子进程和父进程的运行是一个异步过程,理论上父进程无法知道子进程的运行状态。

但知道子进程运行状态是一个很合理的需求,所以 UNIX 提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息,就可以得到。

这种机制就是: 在每个进程退出的时候,内核释放该进程的一部分资源,包括打开的文件、占用的内存等,同时仍然为其保留一定的信息(包括进程号 the process ID,退出状态 the termination status of the process,运行时间 the amount of CPU time taken by the process等)。父进程可以通过 wait()/waitpid() 来获取这些信息,然后操作系统才释放。

这样就产生了一个问题,如果父进程不调用 wait()/waitpid() 的话,那么保留的信息就不会释放,其进程号就会一直被占用,就像僵尸一样,所以把这些进程称为僵尸进程。

僵尸进程的坏处

上面说到僵尸进程由于父进程不回收系统保留的信息而一直占用着系统资源,其中有一项叫做进程描述符。系统通过分配它来启动一个进程。

但是系统所能使用的进程号是有限的,如果存在大量的僵尸进程,系统将因为没有可用的进程号而导致系统不能产生新的进程。

如何查看僵尸进程

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

避免僵尸进程的方法

通过WAIT/WAITPID系统

在 pcntl 中,父进程可以通过 pcntl_wait() 和 pcntl_waitpid() 函数来等待子进程结束,并回收。

 <?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(10);
}else{
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;
}

在子进程退出后,父进程收到了 SIGCHLD 信号,回调了 sig_func 函数,在内部执行了 pcntl_wait 函数,回收了子进程。

通过操作系统信号

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

 <?php
declare(ticks = 1); pcntl_signal(SIGCHLD, SIG_IGN); $pid = pcntl_fork(); if($pid == -1) {
die('fork error');
} else if ($pid) {
for(;;) {
sleep(3);
}
} else {
echo "child \r\n";
exit;
}

PHP的多进程--防止僵尸进程(转)的更多相关文章

  1. Linux 网络编程详解六(多进程服务器僵尸进程解决方案)

    小结:在点对点p2p程序中,服务器端子程序退出,子进程会主动发送信号,关闭父进程,但是这种模式导致服务器只能支持一个客户端连接,本章节中使用新的框架,子进程退出,不主动发送信号关闭父进程,而是父进程安 ...

  2. PHP多进程编之僵尸进程问题

    上一篇说到了使用pcntl_fork函数可以让PHP实现多进程并发或者异步处理的效果.那么问题是我们产生的进程需要去控制,而不能置之不理.最基本的方式就是fork进程和杀死进程. 通过利用pcntl_ ...

  3. php多进程pcntl学习-僵尸进程

    上个月写的文章,php多进程pcntl学习(一)现在发现并不完整,因为虽然提到了关闭子进程,但是并没有回收子进程,简单的说就是当子进程比父进程先退出,而父进程没对其做任何处理的时候,子进程将会变成僵尸 ...

  4. PHP多进程编程之僵尸进程问题

    上一篇说到了使用pcntl_fork函数可以让PHP实现多进程并发或者异步处理的效果.那么问题是我们产生的进程需要去控制,而不能置之不理.最基本的方式就是fork进程和杀死进程. 通过利用pcntl_ ...

  5. php多进程 防止出现僵尸进程

    对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题. 僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程.任何进程在退出前(使用exit退出) ...

  6. php多进程防止出现僵尸进程

    对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题. 僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程.任何进程在退出前(使用exit退出) ...

  7. 多进程wait、僵尸进程、孤儿进程、prctl

    1.概念 1.孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程.孤儿进程将被init进程(进程号为1)所收养,从而保证每个进程都会有一个父进程.而Init进程会自 ...

  8. python学习笔记—— 多进程中的 孤儿进程和僵尸进程

    1 基本概述 1.1 孤儿进程和僵尸进程 父进程创建子进程后,较为理想状态是子进程结束,父进程回收子进程并释放子进程占有的资源:而实际上,父子进程是异步过程,两者谁先结束是无顺的,一般可以通过父进程调 ...

  9. C/C++网络编程8——多进程服务器端之销毁僵尸进程

    上一节提到,当子进程执行结束,父进程还在执行,在父进程结束之前子进程会成为僵尸进程,那么怎么销毁僵尸进程呢?父进程主动接收子进程的返回值. 销毁僵尸进程的方法: 1:使用wait函数 2:使用wait ...

随机推荐

  1. 安装安卓SDK和JDK的简便方法

    直接在VS的安装程序里选:使用.NET的移动开发,其中就包括了安卓SDK,JAVA SE等 另外:自己手动安装SDK时,不要选模拟器相关的东西,太大了,如果每个版本都选,安装下来上100G以上

  2. git reset 版本回退

    git log 查看所有提交信息. commit 67692318180bed6b2a17db0708cfbe0231e33db3 (HEAD -> master) Author: kingBo ...

  3. Go语言学习笔记(2)

    数组 var a [2]string a[0] = "Hello" a[1] = "World" primes := [6]int{2, 3, 5, 7, 11 ...

  4. mycat 单库分表

    上次把mycat的读写分离搞定了,这次试下单库分表,顾名思义就是在一个库里把一个表拆分为多个 需要配置的配置文件为 schema.xml 配置内容如下 <!DOCTYPE mycat:schem ...

  5. RabbitMQ.Net 应用(1)

    风浪子 概述 MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Pu ...

  6. jQuery+php实现二级联动

    public function liandong(){ $arr = Db::table("city")->where("pid=0")->sele ...

  7. VC/VS2010中快捷键

    序号 热键 备注 1 F7 编译 2 Ctrl + F7 链接 3 Ctrl + F5 运行 4 F9 在光标处设置断点 5 F10 单步跟踪 6 F11 进入函数内部跟踪 7 Shift + F11 ...

  8. springmvc文件上传功能

    步骤: 1.在mvc配置文件中添加 2.在控制层的写法: 先在项目目录中添加一个文件夹 再在控制层写上传文件的代码(ps:图片保存在项目中的,并不是保存在文件服务器中) 上传文件的jsp 展示图片的j ...

  9. centos7升级内核

    载入elrepo源,搜索内核更新资源,并进行更新操作. 具体实验步骤: # 载入公钥rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org ...

  10. destructuring assignment

    [destructuring assignment] The destructuring assignment syntax is a JavaScript expression that makes ...