资讯专栏INFORMATION COLUMN

PHP多进程系列笔记(一)

ddongjian0000 / 1994人阅读

摘要:用于创建子进程。该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。注意处需要注意子进程需要防止子进程也进入循环。如果没有,最终创建的子进程不只个。

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

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

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

Note: 此扩展在 Windows 平台上不可用。
pcntl_fork
int pcntl_fork ( void )

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

fork.php


命令行运行:

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

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

[root@9355490fe5da /]# ps -ef | grep php
root       105     1  0 16:46 pts/0    00:00:00 php fork.php
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了。

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

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

我们修改代码:


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

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

示例:

 0){
            sleep(10);//此处为了方便看效果,实际不需要
            break;
        }
    } 
}else{
    $id = getmypid();   
    echo "Child process,pid {$id}
";   
    sleep(2); 
}

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

 $pid) {
        // $res = pcntl_wait($status, WNOHANG);
        $res = pcntl_waitpid($pid, $status, WNOHANG);//#3
        if ($res == -1 || $res > 0){
            echo time()." Child process exit,pid {$pid}
";   
            unset($child_pids[$key]);
        }else{
            // echo time()." Wait End,pid {$pid}
";   //#4
        }
    }
    
} 

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

$ php fork.1.php 
1528637334 Parent process,pid 6600, child pid 6601
1528637334 Child process,pid 6601,sleep 2
1528637334 Parent process,pid 6600, child pid 6602
1528637334 Child process,pid 6602,sleep 2
1528637334 Parent process,pid 6600, child pid 6603
1528637334 Child process,pid 6603,sleep 1
1528637336 Child process exit,pid 6601
1528637336 Child process exit,pid 6602
1528637336 Child process exit,pid 6603

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

$ php fork.1.php 
1528637511 Parent process,pid 6695, child pid 6696
1528637511 Child process,pid 6696,sleep 2
1528637511 Parent process,pid 6695, child pid 6697
1528637511 Child process,pid 6697,sleep 1
1528637511 Parent process,pid 6695, child pid 6698
1528637511 Child process,pid 6698,sleep 3
1528637512 Child process exit,pid 6697
1528637513 Child process exit,pid 6696
1528637514 Child process exit,pid 6698

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

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

检测status函数

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

代码片段:

while(1){
$res = pcntl_wait($status);
if ($res == -1 || $res > 0){

    if(!pcntl_wifexited($status)){
        //进程非正常退出
        echo "service exit unusally; pid is $pid
";
    }else{
        //获取进程终端的退出状态码;
        $code = pcntl_wexitstatus($status);
        echo "service exit code: $code;pid is $pid 
";
    }

    if(pcntl_wifsignaled($status)){
        //不是通过接受信号中断
        echo "service term not by signal;pid is $pid 
";
    }else{
        $signal = pcntl_wtermsig($status);
        echo "service term by signal $signal;pid is $pid
";
    }

    if(pcntl_wifstopped($status)){
        echo "service stop not unusally;pid is $pid 
";
    }else{
        $signal = pcntl_wstopsig($status);
        echo "service stop by signal $signal;pid is $pid
";
    }

    break;
}
参考

1、php多进程 防止出现僵尸进程
https://www.cnblogs.com/jkko1...
2、PCNTL函数族--PHP多进程编程 (转)
https://www.cnblogs.com/zox20...


防盗版声明:本文系原创文章,原发布于公众号飞鸿影的博客(fhyblog)及博客园,转载需作者同意。


欢迎关注公众号及时获取最新文章推送!


推荐!每月仅需$2.5,即可拥有配置SSD的VPS!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28874.html

相关文章

  • PHP进程系列笔记(二)

    摘要:任何进程在退出前使用退出都会变成僵尸进程用于保存进程的状态等信息,然后由进程接管。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。那么子进程结束后,没有回收,就产生僵尸进程了。本小节我们通过安装信号处理函数来解决僵尸进程问题。 上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识。 僵尸(zombie)进程 这里说下僵尸进程...

    CatalpaFlat 评论0 收藏0
  • PHP进程系列笔记(五)

    摘要:消息队列更常见的用途是主进程分配任务,子进程消费执行。子进程前面加了个,这是为了防止父进程还未往消息队列中加入内容直接退出。 前面几节都是讲解pcntl扩展实现的多进程程序。本节给大家介绍swoole扩展的swoole_process模块。 swoole多进程 swoole_process 是swoole提供的进程管理模块,用来替代PHP的pcntl扩展。 首先,确保安装的swoole...

    qianfeng 评论0 收藏0
  • PHP进程系列笔记(四)

    摘要:本节主要讲解常用函数和进程池的概念,也会涉及到守护进程的知识。所以任何时候,建议预先创建好进程,也就是使用进程池的方式实现。 本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识。本节难度较低。 Posix常用函数 posix_kill 向指定pid进程发送信号。成功时返回 TRUE , 或者在失败时返回 FALSE 。 bool posix_kill ( int $...

    Cc_2011 评论0 收藏0
  • PHP 进程系列笔记(三)

    摘要:本节讲解几个多进程的实例。新开终端,我们使用命令查看进程可以看到个进程个主进程,个子进程。使用命令结束子进程,主进程会重新拉起一个新的子进程。 本节讲解几个多进程的实例。 多进程实例 Master-Worker结构 下面例子实现了简单的多进程管理: 支持设置最大子进程数 Master-Worker结构:Worker挂掉,Master进程会重新创建一个

    focusj 评论0 收藏0
  • Swoole笔记

    摘要:修复添加超过万个以上定时器时发生崩溃的问题增加模块,下高性能序列化库修复监听端口设置无效的问题等。线程来处理网络事件轮询,读取数据。当的三次握手成功了以后,由这个线程将连接成功的消息告诉进程,再由进程转交给进程。此时进程触发事件。 本文示例代码详见:https://github.com/52fhy/swoo...。 简介 Swoole是一个PHP扩展,提供了PHP语言的异步多线程服务器...

    SHERlocked93 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<