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 相关文章推荐
计数器详细设计
Oct 09 PHP
php和数据库结合的一个简单的web实例 代码分析 (php初学者)
Jul 28 PHP
PHP下利用shell后台运行PHP脚本,并获取该脚本的Process ID的代码
Sep 19 PHP
PHP中数组定义的几种方法
Sep 01 PHP
PHP fastcgi模式上传大文件(大约有300多K)报错
Sep 28 PHP
基于GD2图形库的PHP生成图片缩略图类代码分享
Feb 08 PHP
浅析PHP中call user func()函数及如何使用call user func调用自定义函数
Nov 05 PHP
Symfony2之session与cookie用法小结
Mar 18 PHP
php简单实现短网址(短链)还原的方法(测试可用)
May 09 PHP
php实现文章置顶功能的方法
Oct 20 PHP
php连接mysql数据库
Mar 21 PHP
解决laravel5中auth用户登录其他页面获取不到登录信息的问题
Oct 08 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
【COS正片】蕾姆睡衣cos,纯洁可爱被治愈了 cn名濑弥七
2020/03/02 日漫
第二节 对象模型 [2]
2006/10/09 PHP
php除数取整示例
2014/04/24 PHP
thinkphp实现发送邮件密码找回功能实例
2014/12/01 PHP
ThinkPHP数据操作方法总结
2015/09/28 PHP
php面向对象程序设计入门教程
2019/06/22 PHP
Laravel框架实现即点即改功能的方法分析
2019/10/31 PHP
Javascript实现的分页函数
2006/12/22 Javascript
能说明你的Javascript技术很烂的五个原因分析
2011/10/28 Javascript
js实现不提交表单获取单选按钮值的方法
2015/08/21 Javascript
ztree获取选中节点时不能进入可视区域出现BUG如何解决
2015/12/03 Javascript
ASP.NET jquery ajax传递参数的实例
2016/11/02 Javascript
vue+vuex+axios实现登录、注册页权限拦截
2018/03/09 Javascript
Mint-UI时间组件起始时间问题及时间插件使用
2018/08/20 Javascript
vue2 v-model/v-text 中使用过滤器的方法示例
2019/05/09 Javascript
利用JS响应式修改vue实现页面的input值
2019/09/02 Javascript
vue $router和$route的区别详解
2020/12/02 Vue.js
javascript实现数字时钟效果
2021/02/06 Javascript
[53:10]完美世界DOTA2联赛决赛日 FTD vs GXR 第二场 11.08
2020/11/11 DOTA
详解Python实现按任意键继续/退出的功能
2016/08/19 Python
Python 常用的安装Module方式汇总
2017/05/06 Python
Python科学计算包numpy用法实例详解
2018/02/08 Python
python 通过logging写入日志到文件和控制台的实例
2018/04/28 Python
python将字典列表导出为Excel文件的方法
2019/09/02 Python
python解析命令行参数的三种方法详解
2019/11/29 Python
Python绘制数码晶体管日期
2021/02/19 Python
MCM英国官网:奢侈皮具制品
2017/04/18 全球购物
加拿大票务网站:Ticketmaster加拿大
2017/07/17 全球购物
意大利文具和办公产品在线商店:Y-Office
2020/02/27 全球购物
一些Unix笔试题和面试题
2013/01/22 面试题
广州喜创信息技术有限公司JAVA软件工程师笔试题
2012/10/17 面试题
师范学院美术系毕业生自我鉴定
2014/01/29 职场文书
探亲邀请信范文
2014/01/30 职场文书
2014年社区工会工作总结
2014/12/18 职场文书
大学文艺委员竞选稿
2015/11/19 职场文书
Python pandas读取CSV文件的注意事项(适合新手)
2021/06/20 Python