本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识。

PCNTL在PHP中进程控制支持默认是关闭的。您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或CLI版本以打开进程控制支持。

如果自带的PHP没有安装pcntl扩展,可以下载相同版本的源码,进入ext/pcntl使用phpize编译安装。

Note: 此扩展在 Windows 平台上不可用。

pcntl_fork

  1. int pcntl_fork ( void )

用于创建子进程。成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0。失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。

fork.php

  1. <?php
  2. $pid = pcntl_fork();
  3. if($pid == -1){
  4. //错误处理:创建子进程失败时返回-1.
  5. die( 'could not fork' );
  6. }elseif($pid){
  7. //父进程会得到子进程号,所以这里是父进程执行的逻辑
  8. $id = getmypid();
  9. echo "Parent process,pid {$id}, child pid {$pid}\n";
  10. }else{
  11. //子进程得到的$pid为0, 所以这里是子进程执行的逻辑
  12. $id = getmypid();
  13. echo "Child process,pid {$id}\n";
  14. sleep(10);
  15. }

命令行运行:

  1. $ php fork.php
  2. Parent process,pid 98, child pid 99
  3. Child process,pid 99

该例里父进程还没有来得及等子进程运行完毕就自动退出了,子进程由 init进程接管。通过 ps -ef | grep php 看到子进程还在运行:

  1. [root@9355490fe5da /]# ps -ef | grep php
  2. root 105 1 0 16:46 pts/0 00:00:00 php fork.php
  3. root 107 27 0 16:46 pts/1 00:00:00 grep php

子进程成为孤立进程,ppid(父进程id)变成1了。如果在父进程里也加个sleep(5),你会看到子进程ppid本来是大于1的,后来就变成1了。

注:如果是docker环境,孤立进程的ppid可能是0。

pcntl_wait

pcntl_wait()函数用来让父进程等待子进程退出,默认情况下会阻塞主进程。

阻塞模式

紧接着上面的例子,如果想等子进程运行结束后父进程再退出,该怎么办?那就用到pcntl_wait了。

  1. int pcntl_wait ( int &$status [, int $options = 0 ] )

该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。

我们修改代码:

  1. <?php
  2. $pid = pcntl_fork();
  3. if($pid == -1){
  4. exit("fork fail");
  5. }elseif($pid){
  6. $id = getmypid();
  7. echo "Parent process,pid {$id}, child pid {$pid}\n";
  8. pcntl_wait($status);
  9. //pcntl_waitpid($pid, $status);
  10. }else{
  11. $id = getmypid();
  12. echo "Child process,pid {$id}\n";
  13. sleep(10);
  14. }

此时再次运行程序,父进程就会一直等待子进程运行结束然后退出。

pcntl_waitpid()pcntl_wait()功能相同。前者第一个参数支持指定pid参数,当指定-1作为pid的值等同于后者。

  1. int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )

当已知子进程pid的时候,可以使用pcntl_waitpid()

这两个函数返回退出的子进程进程号(>1),发生错误时返回-1,如果提供了 WNOHANG 作为option(wait3可用的系统)并且没有可用子进程时返回0。

返回值为退出的子进程进程号时,想了解如何退出,可以通过 $status状态码反应。

非阻塞模式

pcntl_wait()默认情况下会阻塞主进程,直到子进程执行完毕才继续往下运行。如果设置最后一个参数为常量WNOHANG,那么就不会阻塞主进程,而是继续执行后续代码, 此时 pcntl_waitpid 就会返回0。

示例:

  1. <?php
  2. $pid = pcntl_fork();
  3. if($pid == -1){
  4. exit("fork fail");
  5. }elseif($pid){
  6. $id = getmypid();
  7. echo "Parent process,pid {$id}, child pid {$pid}\n";
  8. while(1){
  9. $res = pcntl_wait($status, WNOHANG);
  10. //$res = pcntl_waitpid($pid, $status, WNOHANG);
  11. if ($res == -1 || $res > 0){
  12. sleep(10);//此处为了方便看效果,实际不需要
  13. break;
  14. }
  15. }
  16. }else{
  17. $id = getmypid();
  18. echo "Child process,pid {$id}\n";
  19. sleep(2);
  20. }

该示例里只有一个子进程,看不出来非阻塞的好处,我们修改一下:

  1. <?php
  2. $child_pids = [];
  3. for($i=0;$i<3; $i++){
  4. $pid = pcntl_fork();
  5. if($pid == -1){
  6. exit("fork fail");
  7. }elseif($pid){
  8. $child_pids[] = $pid;
  9. $id = getmypid();
  10. echo time()." Parent process,pid {$id}, child pid {$pid}\n";
  11. }else{
  12. $id = getmypid();
  13. $rand = rand(1,3);
  14. echo time()." Child process,pid {$id},sleep $rand\n";
  15. sleep($rand); //#1 故意设置时间不一样
  16. exit();//#2 子进程需要exit,防止子进程也进入for循环
  17. }
  18. }
  19. while(count($child_pids)){
  20. foreach ($child_pids as $key => $pid) {
  21. // $res = pcntl_wait($status, WNOHANG);
  22. $res = pcntl_waitpid($pid, $status, WNOHANG);//#3
  23. if ($res == -1 || $res > 0){
  24. echo time()." Child process exit,pid {$pid}\n";
  25. unset($child_pids[$key]);
  26. }else{
  27. // echo time()." Wait End,pid {$pid}\n"; //#4
  28. }
  29. }
  30. }

#3处首先先去掉WNOHANG参数,运行:

  1. $ php fork.1.php
  2. 1528637334 Parent process,pid 6600, child pid 6601
  3. 1528637334 Child process,pid 6601,sleep 2
  4. 1528637334 Parent process,pid 6600, child pid 6602
  5. 1528637334 Child process,pid 6602,sleep 2
  6. 1528637334 Parent process,pid 6600, child pid 6603
  7. 1528637334 Child process,pid 6603,sleep 1
  8. 1528637336 Child process exit,pid 6601
  9. 1528637336 Child process exit,pid 6602
  10. 1528637336 Child process exit,pid 6603

我们看到,6603号进程运行时间最短,但是是最后回收。我们再加上WNOHANG参数,运行:

  1. $ php fork.1.php
  2. 1528637511 Parent process,pid 6695, child pid 6696
  3. 1528637511 Child process,pid 6696,sleep 2
  4. 1528637511 Parent process,pid 6695, child pid 6697
  5. 1528637511 Child process,pid 6697,sleep 1
  6. 1528637511 Parent process,pid 6695, child pid 6698
  7. 1528637511 Child process,pid 6698,sleep 3
  8. 1528637512 Child process exit,pid 6697
  9. 1528637513 Child process exit,pid 6696
  10. 1528637514 Child process exit,pid 6698

6697进程最先回收!说明确实是异步非阻塞的。感兴趣的朋友还可以开启#4处代码,未使用WNOHANG参数的时候,里面的代码是不会运行的。

注意:#2处需要注意子进程需要exit,防止子进程也进入for循环。如果没有exit(),最终创建的子进程不只3个。

检测status函数

在 pcntl_waitpcntl_waitpid两个函数中的$status中存了子进程的状态信息,这个参数可以用于 pcntl_wifexitedpcntl_wifstoppedpcntl_wifsignaledpcntl_wexitstatus、 pcntl_wtermsigpcntl_wstopsigpcntl_waitpid这些函数。

代码片段:

 
  1. while(1){
  2. $res = pcntl_wait($status);
  3. if ($res == -1 || $res > 0){
  4. if(!pcntl_wifexited($status)){
  5. //进程非正常退出
  6. echo "service exit unusally; pid is $pid\n";
  7. }else{
  8. //获取进程终端的退出状态码;
  9. $code = pcntl_wexitstatus($status);
  10. echo "service exit code: $code;pid is $pid \n";
  11. }
  12. if(pcntl_wifsignaled($status)){
  13. //不是通过接受信号中断
  14. echo "service term not by signal;pid is $pid \n";
  15. }else{
  16. $signal = pcntl_wtermsig($status);
  17. echo "service term by signal $signal;pid is $pid\n";
  18. }
  19. if(pcntl_wifstopped($status)){
  20. echo "service stop not unusally;pid is $pid \n";
  21. }else{
  22. $signal = pcntl_wstopsig($status);
  23. echo "service stop by signal $signal;pid is $pid\n";
  24. }
  25. break;
  26. }

PHP多进程系列笔(转)的更多相关文章

  1. PHP多进程系列笔记(一)

    本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识. PCNTL在PHP中进程控制支持默认是关闭的.您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或 ...

  2. PHP多进程系列笔记(五)

    前面几节都是讲解pcntl扩展实现的多进程程序.本节给大家介绍swoole扩展的swoole_process模块. swoole多进程 swoole_process 是swoole提供的进程管理模块, ...

  3. PHP多进程系列笔记(三)

    本节讲解几个多进程的实例. 多进程实例 Master-Worker结构 下面例子实现了简单的多进程管理: 支持设置最大子进程数 Master-Worker结构:Worker挂掉,Master进程会重新 ...

  4. PHP多进程系列笔记(二)

    上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识. 僵尸(zombie)进程 这里说下僵尸进程: 僵尸进程是指的父进程已经退出,而该进程de ...

  5. PHP多进程系列笔记(四)

    本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识.本节难度较低. Posix常用函数 posix_kill 向指定pid进程发送信号.成功时返回 TRUE , 或者在失败时返回 ...

  6. PHP Socket 编程进阶指南

    学习准备 Linux 或者 Mac 环境: 安装有 Sockets 扩展: 了解 TCP/IP 协议. socket函数只是PHP扩展的一部分,编译PHP时必须在配置中添加 --enable-sock ...

  7. 系列3|走进Node.js之多进程模型

    文:正龙(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 之前的文章"走进Node.js之HTTP实现分析"中,大家已经了解 Node.js 是如何处理 HTTP 请求 ...

  8. python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据

    python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据 最近工作太忙了,有一个星期没有更新文章了,本来这一期打算分享一些对龙虎榜数据的分析结果的,现在还没有把数据内的价值很好 ...

  9. ACM_招新笔试题系列——买包子

    招新笔试题系列——买包子 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小华刚到大学,一天早上她替她室友买早餐,一共要N个包子. ...

随机推荐

  1. 松软科技web课堂:SQLServer之MID() 函数

    MID() 函数 MID 函数用于从文本字段中提取字符. SQL MID() 语法 SELECT MID(column_name,start[,length]) FROM table_name 参数 ...

  2. 【Angular】父组件监听子组件事件(传参)

    Angular官方文档Demo地址:>component-interaction#parent-listens-for-child-event 举一个自己在写的项目

  3. JS基础语法---作用域

    作用域:使用范围 全局变量: 声明的变量是使用var声明的, 那么这个变量就是全局变量 全局变量可以在页面的任何位置使用 除了函数以外, 其他的任何位置定义的变量都是全局变量 局部变量:在函数内部定义 ...

  4. 用html,CSS 写一个静态的博客网页

    <!doctype html> <html> <br/><br/><br/> <head> <meta http-equi ...

  5. WPF窗口传递 委托事件

    1.子窗口定义委托事件 public delegate void Btn_Click(int i); public event Btn_Click BtnEvent; 在子窗口使用 BtnEvent( ...

  6. MATLAB实例:非线性曲线拟合

    MATLAB实例:非线性曲线拟合 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 用最小二乘法拟合非线性曲线,给出两种方法:(1)指定非线性函数,(2) ...

  7. 【bzoj4945】[Noi2017]游戏(搜索+2-sat)

    bzoj 洛谷 题意: 现在有\(a,b,c\)三种车,每个赛道可能会存在限制:\(a\)表示不能选择\(a\)类型的赛车,\(b,c\)同理:\(x\)表示该赛道不受限制,但\(x\)类型的个数$\ ...

  8. (day59)十一、CSRF、Auth模块、impotlib模块、settings源码

    目录 一.模拟实现中间件的编程思想 (一)impotlib模块 (二)实现功能的配置使用 二.跨站请求伪造CSRF (一)由来 (二)form表单的CSRF (三)ajax中的CSRF (1)通过da ...

  9. Linux服务器下配置Java环境、JDK

    前言 可以解决问题有/etc/profile与~/.bashrc环境文件区别 文件权限问题,只读readonly Java环境搭建 一.下载JDK包 地址:jdk1.8提取码:gx0b 把文件放到Li ...

  10. Codeforces Round #599 (Div. 1) C. Sum Balance 图论 dp

    C. Sum Balance Ujan has a lot of numbers in his boxes. He likes order and balance, so he decided to ...