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 相关文章推荐
snoopy PHP版的网络客户端提供本地下载
Apr 15 PHP
PHP缓存技术的使用说明
Aug 06 PHP
解析php中的escape函数
Jun 29 PHP
php使用pdo连接报错Connection failed SQLSTATE的解决方法
Dec 15 PHP
php制作动态随机验证码
Feb 12 PHP
用PHP生成excel文件到指定目录
Jun 22 PHP
php析构函数的简单使用说明
Aug 24 PHP
简单谈谈PHP面向对象之标识对象
Jun 27 PHP
PHP使用星号替代用户名手机和邮箱的实现代码
Feb 07 PHP
php curl发送请求实例方法
Aug 01 PHP
Thinkphp 在api开发中异常返回依然是html的解决方式
Oct 16 PHP
PHP设计模式之数据访问对象模式(DAO)原理与用法实例分析
Dec 12 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 CKEditor 上传图片实现代码
2009/11/06 PHP
针对多用户实现头像上传功能PHP代码 适用于登陆页面制作
2016/08/17 PHP
PHP基于GD库的图像处理方法小结
2016/09/27 PHP
自动更新作用
2006/10/08 Javascript
javascript 常用方法总结
2009/06/03 Javascript
Cookie 小记
2010/04/01 Javascript
Javascript倒计时代码
2010/08/12 Javascript
《JavaScript高级程序设计》阅读笔记(三) ECMAScript中的引用类型
2012/02/27 Javascript
jQuery中size()方法用法实例
2014/12/27 Javascript
javascript实现避免页面按钮重复提交
2015/01/08 Javascript
AngularJS基础教程之简单介绍
2015/09/27 Javascript
jQuery文字横向滚动效果的实现代码
2016/05/31 Javascript
JavaScript对Json的增删改属性详解
2016/06/02 Javascript
vue中的非父子间的通讯问题简单的实例代码
2017/07/19 Javascript
JavaScript中防止微信浏览器被整体拖动的方法
2017/08/25 Javascript
Koa项目搭建过程详细记录
2018/04/12 Javascript
在angular 6中使用 less 的实例代码
2018/05/13 Javascript
Vue $emit $refs子父组件间方法的调用实例
2018/09/12 Javascript
vue点击标签切换选中及互相排斥操作
2020/07/17 Javascript
vue npm install 安装某个指定的版本操作
2020/08/11 Javascript
[03:01]2014DOTA2国际邀请赛 小组赛7月13日TOPPLAY
2014/07/14 DOTA
Python标准库之循环器(itertools)介绍
2014/11/25 Python
python2.7到3.x迁移指南
2018/02/01 Python
Python系统监控模块psutil功能与经典用法分析
2018/05/24 Python
Django框架实现的简单分页功能示例
2018/12/04 Python
从列表或字典创建Pandas的DataFrame对象的方法
2019/07/06 Python
Python使用QQ邮箱发送邮件报错smtplib.SMTPAuthenticationError
2019/12/20 Python
python3通过subprocess模块调用脚本并和脚本交互的操作
2020/12/05 Python
通用C#笔试题附答案
2016/11/26 面试题
客运企业隐患排查工作方案
2014/06/06 职场文书
成都人事代理协议书
2014/10/25 职场文书
晚自修旷课检讨书怎么写
2014/11/17 职场文书
2015年党员自评材料
2014/12/17 职场文书
JavaScript+HTML实现学生信息管理系统
2021/04/20 Javascript
MySQL中in和exists区别详解
2021/06/03 MySQL
Linux、ubuntu系统下查看显卡型号、显卡信息详解
2022/04/07 Servers