本系列文章将向大家讲解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){
命令行运行:
$ php fork.php
Parent process,pid 98,child pid 99
Child process,pid 99
该例里父进程还没有来得及等子进程运行完毕就自动退出了,子进程由?init 进程接管。通过?ps -ef | grep php ?看到子进程还在运行:
[root@9355490fe5da /]
子进程成为孤立进程,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,child pid {$pid}n";
pcntl_wait($status);
此时再次运行程序,父进程就会一直等待子进程运行结束然后退出。
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,child pid {$pid}n";
while(1){
$res = pcntl_wait($status,WNOHANG);
该示例里只有一个子进程,看不出来非阻塞的好处,我们修改一下:
<?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,child pid {$pid}n";
}else{
$id = getmypid();
$rand = rand(1,3);
echo time()." Child process,sleep $randn";
sleep($rand);
#3 处首先先去掉WNOHANG 参数,运行:
$ php fork.1.php
1528637334 Parent process,pid 6600,child pid 6601
1528637334 Child process,pid 6601,sleep 2
1528637334 Parent process,child pid 6602
1528637334 Child process,pid 6602,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,child pid 6697
1528637511 Child process,pid 6697,sleep 1
1528637511 Parent process,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)){
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|