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 相关文章推荐
BBS(php &amp; mysql)完整版(八)
Oct 09 PHP
文件系统基本操作类
Nov 23 PHP
利用PHP制作简单的内容采集器的代码
Nov 28 PHP
需要使用php模板的朋友必看的很多个顶级PHP模板引擎比较分析
May 26 PHP
PHP日期时间函数的高级应用技巧
May 16 PHP
使用PHPMyAdmin修复论坛数据库的图文方法
Jan 09 PHP
PHP5中Cookie与 Session使用详解
Apr 30 PHP
php计算两个整数的最大公约数常用算法小结
Mar 05 PHP
微信公众平台开发之天气预报功能
Aug 31 PHP
阿里云Win2016安装Apache和PHP环境图文教程
Mar 11 PHP
php精度计算的问题解析
Jun 21 PHP
phpquery中文手册
Mar 18 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 gbk环境下json_dencode传送来的汉字
2012/11/13 PHP
php中通过DirectoryIterator删除整个目录的方法
2015/03/13 PHP
比较简单的一个符合web标准的JS调用flash方法
2007/11/29 Javascript
jquery 选项卡效果 新手代码
2011/07/08 Javascript
利用jquery包将字符串生成二维码图片
2013/09/12 Javascript
JSuggest自动匹配下拉框使用方法(示例代码)
2013/12/27 Javascript
对JavaScript客户端应用编程的一些建议
2015/06/24 Javascript
实例讲解避免javascript冲突的方法
2016/01/03 Javascript
ES6新特征数字、数组、字符串
2016/10/01 Javascript
微信小程序 MINA文件结构
2016/10/17 Javascript
JS中Array数组学习总结
2017/01/18 Javascript
AngularJS ng-repeat指令中使用track by子语句解决重复数据遍历错误问题
2017/01/21 Javascript
网页中右键功能的实现方法之contextMenu的使用
2017/02/20 Javascript
详解Angular2组件之间如何通信
2017/06/22 Javascript
jquery引入外部CDN 加载失败则引入本地jq库
2018/05/23 jQuery
Node.js一行代码实现静态文件服务器的方法步骤
2019/05/07 Javascript
vue中watch的用法汇总
2020/12/28 Vue.js
[01:54]胎教DOTA2 准妈妈玩家现身中国区预选赛
2016/06/26 DOTA
Python中定时任务框架APScheduler的快速入门指南
2017/07/06 Python
Python django使用多进程连接mysql错误的解决方法
2018/10/08 Python
python程序 创建多线程过程详解
2019/09/23 Python
Python OpenCV视频截取并保存实现代码
2019/11/30 Python
python 实现dict转json并保存文件
2019/12/05 Python
使用python+whoosh实现全文检索
2019/12/09 Python
Python GUI库PyQt5图形和特效样式QSS介绍
2020/02/25 Python
Python3 hashlib密码散列算法原理详解
2020/03/30 Python
宝拉珍选澳大利亚官方购物网站:Paula’s Choice澳大利亚
2016/09/13 全球购物
ET Mall东森购物网:东森严选
2017/03/06 全球购物
营业员实习自我鉴定
2013/12/07 职场文书
银行存款证明样本
2014/01/17 职场文书
2014年仓管员工作总结
2014/11/18 职场文书
2014年商场工作总结
2014/11/22 职场文书
穆斯林的葬礼读书笔记
2015/06/26 职场文书
婚宴主持词
2015/06/30 职场文书
学习社交礼仪心得体会
2016/01/22 职场文书
springboot拦截器无法注入redisTemplate的解决方法
2021/06/27 Java/Android