本系列文章将向大家讲解pcntl_*
系列函数,从而更深入的理解进程相关知识。
pcntl在php中进程控制支持默认是关闭的。您需要使用
--enable-pcntl
配置选项重新编译php的 cgi或cli版本以打开进程控制支持。
如果自带的php没有安装pcntl扩展,可以下载相同版本的源码,进入ext/pcntl
使用phpize
编译安装。
note: 此扩展在 windows 平台上不可用。
int pcntl_fork ( void )
用于创建子进程。成功时,在父进程执行线程内返回产生的子进程的pid,在子进程执行线程内返回0。失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个php错误。
fork.php
<?php $pid = pcntl_fork();if($pid == -1){ //错误处理:创建子进程失败时返回-1. die( 'could not fork' );}elif($pid){ //父进程会得到子进程号,所以这里是父进程执行的逻辑 $id = getmypid(); echo "parent process,pid {$id}, child pid {$pid}\n"; }el{ //子进程得到的$pid为0, 所以这里是子进程执行的逻辑 $id = getmypid(); echo "child process,pid {$id}\n"; more的原级 sleep(10); }
命令行运行:
$ php fork.phpparent process,pid 98, child pid 99child process,pid 99
该例里父进程还没有来得及等子进程运行完毕就自动退出了,子进程由init
进程接管。通过ps -ef | grep php
看到子进程还在运行:
[root@9355490fe5da /]# ps -ef | grep phproot 105 1 0 16:46 pts/0 00:00:00 php fork.phproot 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
了。
int pcntl_wait ( int &$status [, int $options =0] )
该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。
我们修改代码:
<?php $pid = pcntl_fork();if($pid == -1){ exit("fork fail");}elif($pid){ $id = getmypid(); echo "parent process,pid {$id}, child pid {$pid}\n"; pcntl_wait($status); //pcntl_waitpid($pid, $status);}el{ $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。
返回值为退出的子进程进程号时,想了解如何退出,可以通过$st可爱的男生网名atus
状态码反应。
pcntl_wait()
默认情况下会阻塞主进程,直到子进程执行完毕才继续往下运行。如果设置最后一个参数为常量wnohang
,那么就不会阻塞主进程,而是继续执行后续代码, 此时pcntl_waitpid
就会返回0。
示例:
<?php $pid = pcntl_fork();if($pid == -1){ exit("fork fail");}elif($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; } } }el{ $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"); }elif($pid){ $child_pids[] = $pid; $id = getmypid(); echo time()负责培养人意见." parent process,pid {$id}, child pid {$pid}\n"; }el{ $id = getmypid(); $rand = rand(1,3); echo time()." child process,pid {$id},sleep $rand\n"; sleep($rand); //#1 故意设置时间不一样 exithave的意思();//#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"; unt($child_pids[$key]); }el{ // echo time()." wait end,pid {$pid}\n"; //#4 } } }
#3
处首先先去掉wnohang
参数,运行:
$ php fork.1.php 1528637334 parent process,pid 6600, child pid 66011528637334 child process,pid 6601,sleep 21528637334 parent process,pid 6600, child pid 66021528637334 chi雕刻时间ld process,pid 6602,sleep 21528637334 parent process,pid 6600, child pid 66031528637334 child process,pid 6603,sleep 11528637336 child process exit,pid 66011528637336 child process exit,pid 66021528637336 child process exit,pid 6603
我们看到,6603号进程运行时间最短,但是是最后回收。我们再加上wnohang
参数,运行:
$ php fork.1.php 1528637511 parent process,pid 6695, child pid 66961528637511 child process,pid 6696,sleep 21528637511 parent process,pid 6695, child pid 66971528637511 child process,pid 6697,sleep 11528637511 parent process,pid 6695, child pid 66981528637511 child process,pid 6698,sleep 31528637512 child process exit,pid 66971528637513 child process exit,pid 66961528637514 child process exit,pid 6698
6697进程最先回收!说明确实是异步非阻塞的。感兴趣的朋友还可以开启#4
处代码,未使用wnohang
参数的时候,里面的代码是不会运行的。
注意:#2
处需要注意子进程需要exit,防止子进程也进入for循环。如果没有exit()
,最终创建的子进程不只3个。
在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 "rvice exit unusally; pid is $pid\n"; }el{ //获取进程终端的退出状态码; $code = pcntl_wexitstatus($status); echo "rvice exit code: $code;pid is $pid \n"; } if(pcntl_wifsignaled($status)){ //不是通过接受信号中断 echo "rvice term not by signal;pid is $pid \n"; }el{ $signal = pcntl_wtermsig($status); echo "rvice term by signal $signal;pid is $pid\n"; } if(pcntl_wifstopped($status)){ echo "rvice stop not unusally;pid is $pid \n"; }el{ $signal = pcntl_wstopsig($status); echo "rvice stop by signal $signal;pid is $pid\n"; } break;}
本文发布于:2023-04-07 16:57:44,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/186b9d8afca89f48d0004dc010e926bf.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:PHP多进程系列笔(转).doc
本文 PDF 下载地址:PHP多进程系列笔(转).pdf
留言与评论(共有 0 条评论) |