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 组件化编程技巧
Jun 06 PHP
PHP Undefined index报错的修复方法
Jul 17 PHP
php程序的国际化实现方法(利用gettext)
Aug 14 PHP
php 判断数组是几维数组
Mar 20 PHP
php数组(array)输出的三种形式详解
Jun 05 PHP
PHP中使用sleep函数实现定时任务实例分享
Aug 21 PHP
php中使用base HTTP验证的方法
Apr 20 PHP
php无序树实现方法
Jul 28 PHP
WordPress中注册菜单与调用菜单的方法详解
Dec 18 PHP
PHP文件上传问题汇总(文件大小检测、大文件上传处理)
Dec 24 PHP
删除PHP数组中头部、尾部、任意元素的实现代码
Apr 10 PHP
yii框架结合charjs实现统计30天数据的方法
Apr 04 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第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
php数组相加 array(“a”)+array(“b”)结果还是array(“a”)
2012/09/19 PHP
深入PHP与浏览器缓存的分析
2013/06/03 PHP
php通过curl模拟登陆DZ论坛
2015/05/11 PHP
PHP自定义序列化接口Serializable用法分析
2017/12/29 PHP
PHP设计模式之工厂模式(Factory Pattern)的讲解
2019/03/21 PHP
图片自动更新(说明)
2006/10/02 Javascript
jquery select(列表)的操作(取值/赋值)
2011/03/16 Javascript
FF火狐下获取一个元素同类型的相邻元素实现代码
2012/12/15 Javascript
jQuery插件开发基础简单介绍
2013/01/07 Javascript
js字符串完全替换函数分享
2014/12/03 Javascript
AngularJS过滤器filter用法总结
2016/12/13 Javascript
JavaScript在控件上添加倒计时功能的实现代码
2017/07/04 Javascript
JavaScript面向对象精要(上部)
2017/09/12 Javascript
JS运动改变单物体透明度的方法分析
2018/01/23 Javascript
Vue开发实现吸顶效果的示例代码
2018/08/21 Javascript
bootstrap table实现合并单元格效果
2018/12/24 Javascript
浅析vue中的MVVM实现原理
2019/03/04 Javascript
[03:37]2016完美“圣”典 风云人物:Mikasa专访
2016/12/07 DOTA
python之yield表达式学习
2014/09/02 Python
Python3实现将文件树中所有文件和子目录归档到tar压缩文件的方法
2015/05/22 Python
Python中创建字典的几种方法总结(推荐)
2017/04/27 Python
使用pandas read_table读取csv文件的方法
2018/07/04 Python
浅析python的Lambda表达式
2019/02/27 Python
Django实现从数据库中获取到的数据转换为dict
2020/03/27 Python
Python OpenCV读取中文路径图像的方法
2020/07/02 Python
国际旅客访问北美最大的汽车租赁提供商:Alamo Rent A Car
2018/06/13 全球购物
BabyBjörn婴儿背带法国官网:BabyBjorn法国
2018/06/16 全球购物
生物科学系大学生的自我评价
2013/12/20 职场文书
运动会通讯稿400字
2014/01/28 职场文书
工作检讨书大全
2015/01/26 职场文书
2015年工程师工作总结
2015/04/30 职场文书
家装电话营销开场白
2015/05/29 职场文书
继续教育心得体会(共6篇)
2016/01/19 职场文书
廉洁自律承诺书2016
2016/03/25 职场文书
nginx静态资源的服务器配置方法
2022/07/07 Servers