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 park、unpark、ord 函数使用方法(二进制流接口应用实例)
Oct 19 PHP
PHP 常用数组内部函数(Array Functions)介绍
Jun 05 PHP
PHP页面中文乱码分析
Oct 29 PHP
适用于抽奖程序、随机广告的PHP概率算法实例
Apr 09 PHP
php微信浏览器分享设置以及回调详解
Aug 01 PHP
php的api数据接口书写实例(推荐)
Sep 22 PHP
PHP登录(ajax提交数据和后台校验)实例分享
Dec 29 PHP
php指定长度分割字符串str_split函数用法示例
Jan 30 PHP
Laravel 5.5 的自定义验证对象/类示例代码详解
Aug 29 PHP
PHP htmlspecialchars() 函数实例代码及用法大全
Sep 18 PHP
PHP设计模式之抽象工厂模式实例分析
Mar 25 PHP
PHP创建XML接口示例
Jul 04 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调用Java对象的方法
2006/10/09 PHP
php一些公用函数的集合
2008/03/27 PHP
PHP版国家代码、缩写查询函数代码
2011/08/14 PHP
脚本收藏iframe
2006/07/21 Javascript
js 操作select相关方法函数
2009/12/06 Javascript
捕获键盘事件(且兼容各浏览器)
2013/07/03 Javascript
Extjs 4.x 得到form CheckBox 复选框的值
2014/05/04 Javascript
JS中attr和prop属性的区别以及优先选择示例介绍
2014/06/30 Javascript
js实现加载更多功能实例
2016/10/27 Javascript
jQuery插件fullPage.js实现全屏滚动效果
2016/12/02 Javascript
jquery表单验证实例仿Toast提示效果
2017/03/03 Javascript
js实现放大镜特效
2017/05/18 Javascript
基于Vue框架vux组件库实现上拉刷新功能
2017/11/28 Javascript
AngularJs1.x自定义指令独立作用域的函数传入参数方法
2018/10/09 Javascript
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
2019/08/23 Javascript
python解析xml模块封装代码
2014/02/07 Python
Python实现Linux下守护进程的编写方法
2014/08/22 Python
python之wxPython应用实例
2014/09/28 Python
详解python进行mp3格式判断
2016/12/23 Python
Python实现读取json文件到excel表
2017/11/18 Python
unittest+coverage单元测试代码覆盖操作实例详解
2018/04/04 Python
python中利用h5py模块读取h5文件中的主键方法
2018/06/05 Python
Python3.5面向对象编程图文与实例详解
2019/04/24 Python
Python批量生成幻影坦克图片实例代码
2019/06/04 Python
CSS3的Border-radius轻松制作圆角
2012/12/24 HTML / CSS
手机端用rem+scss做适配的详解
2017/11/15 HTML / CSS
什么是.net的Remoting技术
2016/07/08 面试题
致200米运动员广播稿
2014/02/06 职场文书
爱之链教学反思
2014/04/30 职场文书
十岁生日答谢词
2015/01/05 职场文书
2015年党风廉政承诺书
2015/01/22 职场文书
销售督导岗位职责
2015/04/10 职场文书
公司晚宴祝酒词
2015/08/11 职场文书
MySQL数据库索引的最左匹配原则
2021/11/20 MySQL
iOS 16进一步确认,一共支持16款iPhone
2022/04/28 数码科技
mysql sql常用语句大全
2022/06/21 MySQL