本系列文章將向你們講解pcntl_*
系列函數,從而更深刻的理解進程相關知識。php
PCNTL在PHP中進程控制支持默認是關閉的。您須要使用
--enable-pcntl
配置選項從新編譯PHP的 CGI或CLI版本以打開進程控制支持。
若是自帶的PHP沒有安裝pcntl擴展,能夠下載相同版本的源碼,進入ext/pcntl
使用phpize
編譯安裝。html
Note: 此擴展在 Windows 平臺上不可用。
int pcntl_fork ( void )
用於建立子進程。成功時,在父進程執行線程內返回產生的子進程的PID,在子進程執行線程內返回0。失敗時,在父進程上下文返回-1,不會建立子進程,而且會引起一個PHP錯誤。docker
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了。spa
注:若是是docker環境,孤立進程的ppid多是0。
pcntl_wait()
函數用來讓父進程等待子進程退出,默認狀況下會阻塞主進程。命令行
緊接着上面的例子,若是想等子進程運行結束後父進程再退出,該怎麼辦?那就用到pcntl_wait
了。線程
int pcntl_wait ( int &$status [, int $options = 0 ] )
該函數阻塞當前進程,只到當前進程的一個子進程退出或者收到一個結束當前進程的信號。 code
咱們修改代碼:
<?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個。
在 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多進程 防止出現殭屍進程
https://www.cnblogs.com/jkko1...
二、PCNTL函數族--PHP多進程編程 (轉)
https://www.cnblogs.com/zox20...
防盜版聲明:本文系原創文章,原發佈於公衆號飛鴻影的博客
(fhyblog)及博客園,轉載需做者贊成。
歡迎關注公衆號及時獲取最新文章推送!
推薦!每個月僅需$2.5,便可擁有配置SSD的VPS!