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 相关文章推荐
基于mysql的论坛(6)
Oct 09 PHP
解析thinkphp中的M()与D()方法的区别
Jun 22 PHP
ThinkPHP之M方法实例详解
Jun 20 PHP
PHP对象递归引用造成内存泄漏分析
Aug 28 PHP
跟我学Laravel之安装Laravel
Oct 15 PHP
PHP正则表达式 /i, /is, /s, /isU等介绍
Oct 23 PHP
一个比较不错的PHP日历类分享
Nov 18 PHP
PHP使用mysqldump命令导出数据库
Apr 14 PHP
PHP中抽象类,接口功能、定义方法示例
Feb 26 PHP
为Plesk PHP7启用Oracle OCI8扩展方法总结
Mar 29 PHP
laravel请求参数校验方法
Oct 10 PHP
使用Git实现Laravel项目的自动化部署
Nov 24 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调用Webservice实例代码
2011/07/29 PHP
PHP 透明水印生成代码
2012/08/27 PHP
使用Smarty 获取当前日期时间和格式化日期时间的方法详解
2013/06/18 PHP
php实现微信公众号无限群发
2015/10/11 PHP
Thinkphp微信公众号支付接口
2016/08/04 PHP
YII框架行为behaviors用法示例
2019/04/26 PHP
jQuery插件Zclip实现完美兼容个浏览器点击复制内容到剪贴板
2015/04/30 Javascript
原生JS实现美图瀑布流布局赏析
2015/09/07 Javascript
AngularJS中的$watch(),$digest()和$apply()区分
2016/04/04 Javascript
JS HTML5拖拽上传图片预览
2016/07/18 Javascript
Javascript在IE和Firefox浏览器常见兼容性问题总结
2016/08/03 Javascript
使用jquery/js获取iframe父子级、同级获取元素的方法
2016/08/05 Javascript
微信小程序前端源码逻辑和工作流
2016/09/25 Javascript
探索Javascript中this的奥秘
2016/12/11 Javascript
基于Vue的ajax公共方法(详解)
2018/01/20 Javascript
layui表格设计以及数据初始化详解
2019/10/26 Javascript
vue实现将数据存入vuex中以及从vuex中取出数据
2019/11/08 Javascript
JS实现简易留言板增删功能
2020/02/08 Javascript
vue数据响应式原理知识点总结
2020/02/16 Javascript
JavaScript实现多个物体同时运动
2020/03/12 Javascript
详解JavaScript 高阶函数
2020/09/14 Javascript
基于JavaScript实现简单抽奖功能代码实例
2020/10/20 Javascript
[58:09]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第三场 6.2
2018/06/03 DOTA
Python基于PycURL自动处理cookie的方法
2015/07/25 Python
Python数据结构之单链表详解
2017/09/12 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
keras训练浅层卷积网络并保存和加载模型实例
2020/07/02 Python
moosejaw旗下的户外商品促销网站:Mountain Steals
2017/02/27 全球购物
澳大利亚女装精品店:Alannah Hill
2020/07/29 全球购物
私有程序集与共享程序集有什么区别
2013/04/05 面试题
少年闰土教学反思
2014/02/22 职场文书
理想点亮人生演讲稿
2014/05/21 职场文书
群众对十八届四中全会的期盼
2014/10/17 职场文书
安全生产感想
2015/08/07 职场文书
Python+腾讯云服务器实现每日自动健康打卡
2021/12/06 Python
MySQL中的全表扫描和索引树扫描
2022/05/15 MySQL