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文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
Jun 13 PHP
PHP UTF8中文字符截断函数代码
Sep 11 PHP
深入解析yii权限分级式访问控制的实现(非RBAC法)
Jun 13 PHP
关于php程序报date()警告的处理(date_default_timezone_set)
Oct 22 PHP
PHP 抽象方法与抽象类abstract关键字介绍及应用
Oct 16 PHP
Laravel 5 框架入门(二)构建 Pages 的管理功能
Apr 09 PHP
如何使用php实现评委评分器
Jul 31 PHP
PHP表单数据写入MySQL数据库的代码
May 31 PHP
Yii中的cookie的发送和读取
Jul 27 PHP
php实现带读写分离功能的MySQL类完整实例
Jul 28 PHP
php 时间time与日期date之间的使用详解及区别
Nov 07 PHP
mysqli扩展无法在PHP7下升级问题的解决
Sep 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的FTP学习(二)[转自奥索]
2006/10/09 PHP
如何使用PHP获取网络上文件
2006/10/09 PHP
php curl选项列表(超详细)
2013/07/01 PHP
开源php中文分词系统SCWS安装和使用实例
2014/04/11 PHP
详解PHP对象的串行化与反串行化
2016/01/24 PHP
Jquery跨域获得Json时invalid label错误的解决办法
2011/01/11 Javascript
js事件绑定快捷键以ctrl+k为例
2014/09/30 Javascript
7个jQuery最佳实践
2016/01/12 Javascript
浅谈js中同名函数和同名变量的执行问题
2017/02/12 Javascript
BootStrap中Table隐藏后显示问题的实现代码
2017/08/31 Javascript
微信小程序实现轮播图效果
2017/09/07 Javascript
微信小程序获取手机系统信息的方法【附源码下载】
2017/12/07 Javascript
详解Nuxt.js Vue服务端渲染摸索
2018/02/08 Javascript
JavaScript代码异常监控实现过程详解
2020/02/17 Javascript
JQuery+drag.js上传图片并且实现图片拖曳
2020/11/18 jQuery
使用Python的Zato发送AMQP消息的教程
2015/04/16 Python
使用Python编写爬虫的基本模块及框架使用指南
2016/01/20 Python
Python3实现并发检验代理池地址的方法
2016/09/18 Python
Python实现通过文件路径获取文件hash值的方法
2017/04/29 Python
关于Python中定制类的比较运算实例
2019/12/19 Python
Python 发送邮件方法总结
2020/08/10 Python
CSS3中的clip-path使用攻略
2015/08/03 HTML / CSS
CSS3中的常用选择器使用示例整理
2016/06/13 HTML / CSS
奥地利智能家居和智能生活网上商店:tink.at
2019/10/07 全球购物
MATCHESFASHION澳大利亚/亚太地区:英国时尚奢侈品电商
2020/01/14 全球购物
澳大利亚最早和最古老的巨型游戏专家:Yardgames
2020/02/20 全球购物
main 函数执行以前,还会执行什么代码
2013/04/17 面试题
青春寄语大全
2014/04/09 职场文书
表扬稿范文
2015/01/17 职场文书
2015年财务部工作总结
2015/04/10 职场文书
田径运动会通讯稿
2015/07/18 职场文书
golang 实现时间戳和时间的转化
2021/05/07 Golang
Html5获取用户当前位置的几种方式
2022/01/18 HTML / CSS
java objectUtils 使用可能会出现的问题
2022/02/28 Java/Android
Golang 结构体数据集合
2022/04/22 Golang
Python find()、rfind()方法及作用
2022/12/24 Python