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 相关文章推荐
攻克CakePHP系列一 连接MySQL数据库
Oct 22 PHP
PHP中执行cmd命令的方法
Oct 11 PHP
PHP操作MySQL事务实例
Nov 05 PHP
CI框架给视图添加动态数据
Dec 01 PHP
php输出指定时间以前时间格式的方法
Mar 21 PHP
CI映射(加载)数据到view层的方法
Mar 28 PHP
Yii2使用自带的UploadedFile实现的文件上传
Jun 20 PHP
详谈php中 strtr 和 str_replace 的效率问题
May 14 PHP
Ajax+PHP实现的删除数据功能示例
Feb 12 PHP
PHP面向对象程序设计之构造方法和析构方法详解
Jun 13 PHP
php实现获取近几日、月时间示例
Jul 06 PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
Dec 11 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
使用Limit参数优化MySQL查询的方法
2008/11/12 PHP
php树型类实例
2014/12/05 PHP
php使用递归计算文件夹大小
2014/12/24 PHP
php准确计算复活节日期的方法
2015/04/18 PHP
给PHP开发者的编程指南 第一部分降低复杂程度
2016/01/18 PHP
jquery星级插件、支持页面中多次使用
2012/03/25 Javascript
jquery仿搜索自动联想功能代码
2014/05/23 Javascript
Javascript访问器属性实例分析
2014/12/30 Javascript
JavaScript模拟实现键盘打字效果
2015/06/29 Javascript
使用JQuery实现智能表单验证功能
2016/03/08 Javascript
Node.js DES加密的简单实现
2016/07/07 Javascript
Angular2实现自定义双向绑定属性
2017/03/22 Javascript
vue.js内部自定义指令与全局自定义指令的实现详解(利用directive)
2017/07/11 Javascript
vue做网页开场视频的实例代码
2017/10/20 Javascript
Angular使用cli生成自定义文件、组件的方法
2018/09/04 Javascript
vue如何根据网站路由判断页面主题色详解
2018/11/02 Javascript
vue模块拖拽实现示例代码
2019/03/09 Javascript
在HTML中使用JavaScript的两种方法
2020/12/24 Javascript
Python中对列表排序实例
2015/01/04 Python
Python的包管理器pip更换软件源的方法详解
2016/06/20 Python
python机器学习理论与实战(六)支持向量机
2018/01/19 Python
使用PyCharm创建Django项目及基本配置详解
2018/10/24 Python
python 多线程重启方法
2019/02/18 Python
django框架实现一次性上传多个文件功能示例【批量上传】
2019/06/19 Python
Pandas之Dropna滤除缺失数据的实现方法
2019/06/25 Python
Python整数对象实现原理详解
2019/07/01 Python
基于python实现的百度音乐下载器python pyqt改进版(附代码)
2019/08/05 Python
如何运行带参数的python脚本
2019/11/15 Python
Python识别处理照片中的条形码
2020/11/16 Python
英国Amara家居法国网站:家居装饰,现代装饰和豪华礼品
2016/12/15 全球购物
服务员自我评价
2014/01/25 职场文书
环卫工人慰问信
2015/02/15 职场文书
体检通知范文
2015/04/21 职场文书
机械生产实习心得体会
2016/01/22 职场文书
PHP解决高并发问题
2021/04/01 PHP
python获取带有返回值的多线程
2022/05/02 Python