本系列文章將向你們講解pcntl_*
系列函數,從而更深刻的理解進程相關知識。php
PCNTL在PHP中進程控制支持默認是關閉的。您須要使用
--enable-pcntl
配置選項從新編譯PHP的 CGI或CLI版本以打開進程控制支持。html
若是自帶的PHP沒有安裝pcntl擴展,能夠下載相同版本的源碼,進入ext/pcntl
使用phpize
編譯安裝。docker
Note: 此擴展在 Windows 平臺上不可用。編程
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了。code
注:若是是docker環境,孤立進程的ppid多是0。htm
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個。
在 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; }
pcntl_wifexited
— 檢查狀態代碼是否表明一個正常的退出。當子進程狀態代碼表明正常退出時返回 TRUE ,其餘狀況返回 FALSE 。pcntl_wexitstatus()
返回一箇中斷的子進程的返回代碼。這個函數僅在函數pcntl_wifexited()
返回 TRUE 時有效。pcntl_wifsignaled
— 檢查子進程狀態碼是否表明因爲某個信號而中斷。若是子進程是因爲某個未捕獲的信號退出的返回 TRUE ,其餘狀況返回 FALSE 。cntl_wtermsig
返回致使子進程中斷的信號編號。這個函數僅在pcntl_wifsignaled()
返回 TRUE 時有效。pcntl_wifstopped
— 檢查子進程當前是否已經中止。若是子進程當前是中止的返回 TRUE ,其餘狀況返回 FALSE 。pcntl_wstopsig ()
返回致使子進程中止的信號編號。這個函數僅在pcntl_wifstopped()
返回 TRUE 時有效。
一、php多進程 防止出現殭屍進程 https://www.cnblogs.com/jkko123/p/6351615.html?utm_source=itdadao&utm_medium=referral 二、PCNTL函數族--PHP多進程編程 (轉) https://www.cnblogs.com/zox2011/archive/2013/02/19/2917448.html