原文地址: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. golang使用Nsq(转)

    为什么要使用Nsq 最近一直在寻找一个高性能,高可用的消息队列做内部服务之间的通讯.一开始想到用zeromq,但在查找资料的过程中,意外的发现了Nsq这个由golang开发的消息队列,毕竟是golan ...

  2. Java中Asm包有什么用?

    ASM能做什么 我们都知道,一般情况下,Class文件是通过javac编译器产生的,然后通过类加载器加载到虚拟机内,再通过执行引擎去执行. 现在我们可以通过ASM的API直接生成符合Java虚拟机规范 ...

  3. MyEclipse反向生成Java代码 ,Reverse Engineering-->Java Source Folder-->没有提供任何条目

    在web项目名称上,手动执行一遍Add Hibernate Capabilities,然后再去DB Browser中进行反向生成pojo实体类和hbm.xml映射文件!,具体生成反向代码的配置如下图所 ...

  4. (Java)怎么去掉字符串数组中重复的值?

    String fdbs = "WXB,WXA,FDA,WXB"; String[] str = fdbs.split(","); Set set = new H ...

  5. Vue2 实例中的 data 属性三种写法与作用:

    <script src="https://unpkg.com/vue/dist/vue.js"></script> <div id="app ...

  6. 在Centos 6.5 X64下切割m3u8

    操作系统:centos 6.5 必需要参考的文章: http://blog.chinaunix.net/uid-23069658-id-4018842.html 准备工作: 安装git yum ins ...

  7. C# HttpWebRequest 模拟下载

    C# web 获取服务端cookie 原文地址:https://www.cnblogs.com/louby/p/5569536.html C#多线程环境下调用 HttpWebRequest 并发连接限 ...

  8. tensflow分布式

    https://blog.csdn.net/CodeMaster_/article/details/76223835 代码解析好文: https://wenku.baidu.com/view/94b2 ...

  9. Python 如何创建2维空数组

    http://blog.csdn.net/yockie/article/details/46127829 myList = [ ( [0] * 3 ) for i in range(4) ] > ...

  10. java语言 找出文章中出现次数最多的单词

    package english; import java.io.File; import java.util.Scanner; import java.io.FileNotFoundException ...