php多进程并发编程防止出现僵尸进程的方法分析


Posted in PHP onFebruary 28, 2020

本文实例讲述了php多进程并发编程防止出现僵尸进程的方法。分享给大家供大家参考,具体如下:

对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题。

僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。 

方法一:

父进程通过pcntl_wait和pcntl_waitpid等函数等待子进程结束

$pid = pcntl_fork();

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

  //pcntl_waitpid($pid, $status);
  
  //非阻塞方式
  //pcntl_wait($status, WNOHANG);

  //pcntl_waitpid($pid, $status, WNOHANG);
} else {
  sleep(3);
  echo "child \r\n";
  exit;
}

方法二:

可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用pcntl_wait或pcntl_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');
} else if ($pid) {
  sleep(10);
} else {
  sleep(3);
  echo "child \r\n";
  exit;
}

如果子进程还没有结束时,父进程就结束了,那么init进程会自动接手这个子进程,进行回收。

如果父进程是循环,又没有安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。 

例如:

<?php
$pid = pcntl_fork();

if($pid == -1) {
  die('fork error');
} else if ($pid) {
  for(;;) {
    sleep(3);
  }
} else {
  echo "child \r\n";
  exit;
}

父进程是个死循环,也没有安装SIGCHLD信号处理函数,子进程结束后。我们通过如下命令查看

> ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'

会发现一个僵尸进程。 

代码改进一下:

<?php
declare(ticks = 1);

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

  pcntl_waitpid(-1, $status, WNOHANG);
}

pcntl_signal(SIGCHLD, 'sig_func');

$pid = pcntl_fork();

if($pid == -1) {
  die('fork error');
} else if ($pid) {
  for(;;) {
    sleep(3);
  }
} 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');
} else if ($pid) {
  for(;;) {
    sleep(3);
  }
} else {
  echo "child \r\n";
  exit;
}

当子进程结束后,SIGCHLD信号并不会发送给父进程,而是通知内核对子进程进行了回收。 

方法四:

通过pcntl_fork两次,也就是父进程fork出子进程,然后子进程中再fork出孙进程,这时子进程退出。那么init进程会接管孙进程,孙进程退出后,init会回收。不过子进程还是需要父进程进行回收。我们把业务逻辑放到孙进程中执行,父进程就不需要pcntl_wait或pcntl_waitpid来等待孙进程(即业务进程)。

<?php
$pid = pcntl_fork();

if($pid == -1) {
  die('fork error');
} else if ($pid) {
  //父进程等待子进程退出
  pcntl_wait($status);
  echo "parent \r\n";
} else {
  //子进程再fork一次,产生孙进程
  $cpid = pcntl_fork();  
  if($cpid == -1) {
    die('fork error');
  } else if ($cpid) {
    //这里是子进程,直接退出
    echo "child \r\n";
    exit;
  } else {
    //这里是孙进程,处理业务逻辑
    for($i = 0; $i < 10; ++$i) {
      echo "work... \r\n";
      sleep(3);
    }
  }
}

子进程退出后,父进程回收子进程,孙进程继续业务逻辑的处理。当孙进程也执行完毕退出后,init回收孙进程。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP的FTP学习(一)[转自奥索]
Oct 09 PHP
PHP 和 XML: 使用expat函数(一)
Oct 09 PHP
php 破解防盗链图片函数
Dec 09 PHP
PHP mcrypt可逆加密算法分析
Jul 19 PHP
Zend Framework中的简单工厂模式 图文
Jul 10 PHP
深入PHP empty(),isset(),is_null()的实例测试详解
Jun 06 PHP
无刷新动态加载数据 滚动条加载适合评论等页面
Oct 16 PHP
ThinkPHP3.1查询语言详解
Jun 19 PHP
php遍历目录方法小结
Mar 10 PHP
PHP getallheaders无法获取自定义头(headers)的问题
Mar 23 PHP
数组任意位置插入元素,删除特定元素的实例
Mar 02 PHP
记Laravel调用Gin接口调用formData上传文件的实现方法
Dec 12 PHP
php 的多进程操作实践案例分析
Feb 28 #PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
Feb 28 #PHP
php的无刷新操作实现方法分析
Feb 28 #PHP
php下的原生ajax请求用法实例分析
Feb 28 #PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
Feb 27 #PHP
php使用fputcsv实现大数据的导出操作详解
Feb 27 #PHP
gearman中任务的优先级和返回状态实例分析
Feb 27 #PHP
You might like
php is_file 判断给定文件名是否为一个正常的文件
2010/05/10 PHP
php验证手机号码(支持归属地查询及编码为UTF8)
2013/02/01 PHP
PHP中is_file不能替代file_exists的理由
2014/03/04 PHP
php设置session值和cookies的学习示例
2014/03/21 PHP
ThinkPHP3.1查询语言详解
2014/06/19 PHP
php数组使用规则分析
2015/02/27 PHP
smarty学习笔记之常见代码段用法总结
2016/03/19 PHP
轻松实现php文件上传功能
2017/02/17 PHP
Google韩国首页图标动画效果
2007/08/26 Javascript
javascript实现显示和隐藏div方法汇总
2015/08/14 Javascript
js中使用使用原型(prototype)定义方法的好处详解
2016/07/04 Javascript
jquery层级选择器的实现(匹配后代元素div)
2016/09/05 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
2016/10/24 Javascript
JS判断指定dom元素是否在屏幕内的方法实例
2017/01/23 Javascript
canvas实现探照灯效果
2017/02/07 Javascript
vue货币过滤器的实现方法
2017/04/01 Javascript
关于Angularjs中自定义指令一些有价值的细节和技巧小结
2018/04/22 Javascript
浅谈Webpack核心模块tapable解析
2018/09/11 Javascript
JS返回页面时自动回滚到历史浏览位置
2018/09/26 Javascript
Vue+webpack实现懒加载过程解析
2020/02/17 Javascript
Element Breadcrumb 面包屑的使用方法
2020/07/26 Javascript
Python使用urllib模块的urlopen超时问题解决方法
2014/11/08 Python
Python最基本的输入输出详解
2015/04/25 Python
Python检测网站链接是否已存在
2016/04/07 Python
使用python对文件中的单词进行提取的方法示例
2018/12/21 Python
Python2.7实现多进程下开发多线程示例
2019/05/31 Python
jupyter 中文乱码设置编码格式 避免控制台输出的解决
2020/04/20 Python
python中判断文件结束符的具体方法
2020/08/04 Python
python中的对数log函数表示及用法
2020/12/09 Python
C#里面如何倒序排列一个数组的元素?
2013/06/21 面试题
护理学专业推荐信
2013/12/03 职场文书
医务工作者先进事迹材料
2014/01/26 职场文书
个人课题方案
2014/05/08 职场文书
2014年预算员工作总结
2014/12/05 职场文书
如何做好员工培训计划?
2019/07/09 职场文书
2019年幼儿园管理条例范本!
2019/07/17 职场文书