PHP多进程系列笔(转)
本系列文章将向大家讲解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
$pid = pcntl_fork();
if($pid == -1){
//错误处理:创建子进程失败时返回-1.
die( 'could not fork' );
}elseif($pid){
//父进程会得到子进程号,所以这里是父进程执行的逻辑
$id = getmypid();
echo "Parent process,pid {$id}, child pid {$pid}\n";
}else{
//子进程得到的$pid为0, 所以这里是子进程执行的逻辑
$id = getmypid();
echo "Child process,pid {$id}\n";
sleep(10);
}
命令行运行:
$ 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 ] )
该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。
我们修改代码:
<?php
$pid = pcntl_fork();
if($pid == -1){
exit("fork fail");
}elseif($pid){
$id = getmypid();
echo "Parent process,pid {$id}, child pid {$pid}\n";
pcntl_wait($status);
//pcntl_waitpid($pid, $status);
}else{
$id = getmypid();
echo "Child process,pid {$id}\n";
sleep(10);
}
此时再次运行程序,父进程就会一直等待子进程运行结束然后退出。
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。
示例:
<?php
$pid = pcntl_fork();
if($pid == -1){
exit("fork fail");
}elseif($pid){
$id = getmypid();
echo "Parent process,pid {$id}, child pid {$pid}\n";
while(1){
$res = pcntl_wait($status, WNOHANG);
//$res = pcntl_waitpid($pid, $status, WNOHANG);
if ($res == -1 || $res > 0){
sleep(10);//此处为了方便看效果,实际不需要
break;
}
}
}else{
$id = getmypid();
echo "Child process,pid {$id}\n";
sleep(2);
}
该示例里只有一个子进程,看不出来非阻塞的好处,我们修改一下:
<?php
$child_pids = [];
for($i=0;$i<3; $i++){
$pid = pcntl_fork();
if($pid == -1){
exit("fork fail");
}elseif($pid){
$child_pids[] = $pid;
$id = getmypid();
echo time()." Parent process,pid {$id}, child pid {$pid}\n";
}else{
$id = getmypid();
$rand = rand(1,3);
echo time()." Child process,pid {$id},sleep $rand\n";
sleep($rand); //#1 故意设置时间不一样
exit();//#2 子进程需要exit,防止子进程也进入for循环
}
}
while(count($child_pids)){
foreach ($child_pids as $key => $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}\n";
unset($child_pids[$key]);
}else{
// echo time()." Wait End,pid {$pid}\n"; //#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_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息,这个参数可以用于 pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、 pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid这些函数。
代码片段:
while(1){
$res = pcntl_wait($status);
if ($res == -1 || $res > 0){
if(!pcntl_wifexited($status)){
//进程非正常退出
echo "service exit unusally; pid is $pid\n";
}else{
//获取进程终端的退出状态码;
$code = pcntl_wexitstatus($status);
echo "service exit code: $code;pid is $pid \n";
}
if(pcntl_wifsignaled($status)){
//不是通过接受信号中断
echo "service term not by signal;pid is $pid \n";
}else{
$signal = pcntl_wtermsig($status);
echo "service term by signal $signal;pid is $pid\n";
}
if(pcntl_wifstopped($status)){
echo "service stop not unusally;pid is $pid \n";
}else{
$signal = pcntl_wstopsig($status);
echo "service stop by signal $signal;pid is $pid\n";
}
break;
}
PHP多进程系列笔(转)的更多相关文章
- PHP多进程系列笔记(一)
本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识. PCNTL在PHP中进程控制支持默认是关闭的.您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或 ...
- PHP多进程系列笔记(五)
前面几节都是讲解pcntl扩展实现的多进程程序.本节给大家介绍swoole扩展的swoole_process模块. swoole多进程 swoole_process 是swoole提供的进程管理模块, ...
- PHP多进程系列笔记(三)
本节讲解几个多进程的实例. 多进程实例 Master-Worker结构 下面例子实现了简单的多进程管理: 支持设置最大子进程数 Master-Worker结构:Worker挂掉,Master进程会重新 ...
- PHP多进程系列笔记(二)
上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识. 僵尸(zombie)进程 这里说下僵尸进程: 僵尸进程是指的父进程已经退出,而该进程de ...
- PHP多进程系列笔记(四)
本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识.本节难度较低. Posix常用函数 posix_kill 向指定pid进程发送信号.成功时返回 TRUE , 或者在失败时返回 ...
- PHP Socket 编程进阶指南
学习准备 Linux 或者 Mac 环境: 安装有 Sockets 扩展: 了解 TCP/IP 协议. socket函数只是PHP扩展的一部分,编译PHP时必须在配置中添加 --enable-sock ...
- 系列3|走进Node.js之多进程模型
文:正龙(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 之前的文章"走进Node.js之HTTP实现分析"中,大家已经了解 Node.js 是如何处理 HTTP 请求 ...
- python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据
python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据 最近工作太忙了,有一个星期没有更新文章了,本来这一期打算分享一些对龙虎榜数据的分析结果的,现在还没有把数据内的价值很好 ...
- ACM_招新笔试题系列——买包子
招新笔试题系列——买包子 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小华刚到大学,一天早上她替她室友买早餐,一共要N个包子. ...
随机推荐
- scrapy实例:爬取天气、气温等
1.创建项目 scrapy startproject weather # weather是项目名称 scrapy crawl spidername开始运行,程序自动使用start_urls构造Requ ...
- Cobalt Strike系列教程第五章:截图与浏览器代理
Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...
- 从0系统学Android-2.4 Activity 的生命周期
本系列文章,参考<第一行代码>,作为个人笔记 更多内容:更多精品文章分类 本系列持续更新中.... 2.4 Activity 的生命周期 掌握 Activity 的生命周期对于开发者来说是 ...
- 27-限制容器的 Block IO
Block IO 是另一种可以限制容器使用的资源.Block IO 指的是磁盘的读写,docker 可通过设置权重.限制 bps 和 iops 的方式控制容器读写磁盘的带宽,下面分别讨论. 注:目前 ...
- 1. Linux-3.14.12内存管理笔记【系统启动阶段的memblock算法(1)】
memblock算法是linux内核初始化阶段的一个内存分配器(它取代了原来的bootmem算法),实现较为简单.负责page allocator初始化之前的内存管理和分配请求. 分析memblock ...
- 自定义v-color指令
在自定义指令的时候,和js行为有关的,最好就写在inserted中去,防止js代码不生效.和样似有关的操作放在bind中去Vue.direactive [d儿 Rai K T V] 没有s哈 < ...
- linux下的set, export, env的区别
set和export的区别 set可以用来显示所有变量的值,而export能将一个变量导出,在其子shell或子进程也可见 export和env的区别 两者的作用是一样的,只是env是一个外部工具 基 ...
- <Matrix> 311 378
311. Sparse Matrix Multiplication 稀疏矩阵的计算.稀疏矩阵的特点是有大量的0,如果采用暴力算法则比然会有很多无意义的计算. C[ i ][ j ] += A[ i ] ...
- CSP2019游记(翻车记)
Preface 也许是人生中最重要的一场比赛了(再进不了冬令营我就没了) 结果不论怎样,想必也都是人生中的一次分水岭吧 从暑假开始到今天的一段时间,自己似乎终于找到了学OI的动力与乐趣.能认识到更多志 ...
- Win32 程序开发入门:一个最简单的Win32程序
一.什么是 Win32 Win32 是指 Microsoft Windows 操作系统的 32 位环境,与 Win64 都为 Windows 常见环境. 这里再介绍下 Win32 Applicatio ...