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的十大要点(上)
Feb 04 PHP
php带密码功能并下载远程文件保存本地指定目录 修改加强版
May 16 PHP
php读取图片内容并输出到浏览器的实现代码
Aug 08 PHP
使用php伪造referer的方法 利用referer防止图片盗链
Jan 20 PHP
php中有关字符串的4个函数substr、strrchr、strstr、ereg介绍和使用例子
Apr 24 PHP
PHP实现阳历到农历转换的类实例
Mar 07 PHP
两种php去除二维数组的重复项方法
Nov 04 PHP
Zend Framework教程之Autoloading用法详解
Mar 08 PHP
PHP中串行化用法示例
Nov 16 PHP
php 微信开发获取用户信息如何实现
Dec 13 PHP
laravel 数据迁移与 Eloquent ORM的实现方法
Apr 12 PHP
thinkphp框架使用JWTtoken的方法详解
Oct 10 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封装的字符串加密解密函数
2015/12/18 PHP
Yii2实现ActiveForm ajax提交
2017/05/26 PHP
Javascript 面向对象 继承
2010/05/13 Javascript
基于Jquery 解决Ajax请求的页面 浏览器后退前进功能,页面刷新功能实效问题
2010/12/11 Javascript
提交表单时执行func方法实现代码
2013/03/17 Javascript
javascript中的取反再取反~~没有意义
2014/04/06 Javascript
jquery 新建的元素事件绑定问题解决方案
2014/06/12 Javascript
JS实现适合于后台使用的动画折叠菜单效果
2015/09/21 Javascript
js实现页面跳转的五种方法推荐
2016/03/10 Javascript
jQuery 3.0 的 setter和getter 模式详解
2016/07/11 Javascript
基于JavaScript实现树形下拉框
2016/08/10 Javascript
JQuery动态添加Select的Option元素实现方法
2016/08/29 Javascript
JS函数进阶之继承用法实例分析
2020/01/15 Javascript
微信小程序实现搜索框功能及踩过的坑
2020/06/19 Javascript
vue集成openlayers加载geojson并实现点击弹窗教程
2020/09/24 Javascript
design vue 表格开启列排序的操作
2020/10/28 Javascript
python线程锁(thread)学习示例
2013/12/04 Python
Python中操作MySQL入门实例
2015/02/08 Python
django框架如何集成celery进行开发
2017/05/24 Python
Python使用Selenium+BeautifulSoup爬取淘宝搜索页
2018/02/24 Python
python实现word 2007文档转换为pdf文件
2018/03/15 Python
Python+OpenCV实现图像融合的原理及代码
2018/12/03 Python
利用python计算时间差(返回天数)
2019/09/07 Python
Tensorflow的常用矩阵生成方式
2020/01/04 Python
tensorflow常用函数API介绍
2020/04/19 Python
keras 回调函数Callbacks 断点ModelCheckpoint教程
2020/06/18 Python
python实现文件分片上传的接口自动化
2020/11/19 Python
德国孕妇装和婴童服装网上商店:bellybutton
2018/04/12 全球购物
澳大利亚领先的时尚内衣零售商:Bras N Things
2020/07/28 全球购物
应届医学毕业生求职信分享
2013/12/02 职场文书
岗位职责的构建方法
2014/02/01 职场文书
致跳远运动员广播稿
2014/02/11 职场文书
广告设计应届生求职信
2014/03/01 职场文书
领导接待方案
2014/03/13 职场文书
财务人员廉洁自律心得体会
2016/01/13 职场文书
gtx1650怎么样 gtx1650显卡相当于什么级别
2022/04/08 数码科技