摘要:多進程的主進程創建了子進程,那主進程如何確認子進程的狀態呢。假如主進程需要根據子進程的狀態做不同的處理呢,這里的狀態包括子進程被掉,或變成僵尸進程等。
PHP 可以通過pcntl 擴展實現多進程編程, 而網上關于如何通過pcntl 創建多進程的在這里就不表了, 我主要說說關于pcntl_fork的一個坑和相關的比較生僻的幾個函數的使用方式, 這也是通過挖坑和填坑得出的結論。
閑言碎語不要講, 直接開始
在實踐中, 我在使用php進行多進程實踐的模型大概如下, 期待的是每個子進程都能創建一個與之對應文件, 最后父進程創建一個屬于父進程的文件,代碼如下(有坑):
$pid_dir = __dir__."/pid_files"; for($i=0; $i<3; $i++){ $pid = pcntl_fork(); if($pid == -1){ var_dump("fork failed"); } if(!$pid){ //子進程代碼 $pid = posix_getpid(); $ppid = posix_getppid(); $r = rand(0,100); //隨機數 touch("$pid_dir/fork_child_process_{$i}_{$ppid}_{$pid}_{$r}"); } } $pid = posix_getpid(); $ppid = posix_getppid(); $r = rand(0,100); //隨機數 touch("$pid_dir/fork_process_pid_{$ppid}_{$pid}_$r");
上面的代碼我通過循環創建3個子進程, 每個進程創建一個文件,完成后到最后, 父進程創建一個屬于他自己的文件,所以, 最后應該會創建出4個文件, 但事實并非如此:
fork_child_process_0_62656_62658_39 fork_child_process_1_62656_62659_51 fork_child_process_1_62658_62660_22 fork_child_process_2_62656_62661_91 fork_child_process_2_62658_62662_22 fork_child_process_2_62659_62663_82 fork_child_process_2_62660_62664_59 fork_process_pid_62225_62656_48 fork_process_pid_62656_62658_22 fork_process_pid_62656_62659_82 fork_process_pid_62656_62661_65 fork_process_pid_62658_62660_59 fork_process_pid_62658_62662_59 fork_process_pid_62659_62663_61 fork_process_pid_62660_62664_10
為何會出現上面的結果, 這是因為在fork之后, 原有的進程會分裂為兩個進程, 一個主進程, 一個子進程, fork后面所有的代碼都是共享的, 雖然通過fork的返回值可以判斷是主進程還是子進程來執行相應的子進程或主進程邏輯,但之后子進程自己又走到了for循環的部分, 子進程自己有創建了子進程, 所以上面看到了多個child_process 文件, 至于為什么是7個,
來分析一下。
=====================華麗的分割線=============================
循環變量$i, 當$i為0時, 會產生一個主進程a(不變)和一個子進程aa,這個子進程創建了一個子進程文件,即fork_child_process_0_62656_62658_39, 主進程a繼續循環, 即$i=1, 又創建了一個子進程ab, 他創建了fork_child_process_1_62656_62659_51, 主進程a繼續循環$i=2, 又創建了一個子進程ac, 他創建了fork_child_process_2_62656_62661_91這里可以看到62656就是主進程a的pid.
至此, 主進程a的循環完畢, 在看看a創建的第一個子進程aa, aa在創建之后, 創建好了上面的子進程文件之后并不會什么也不做, 他也會繼續走for的循環, 而且繼承了主進程a的循環變量, 也就是$i的值為0,所以aa進程下一次的循環的$i就是1, 然后aa繼續創建了子進程aaa,從而創建文件fork_child_process_1_62658_62660_22,aa繼續,$i=2, 又創建了一個子進程aab, 這個子進程創建了文件fork_child_process_2_62658_62662_22, 這里可以看到aaa和aab的ppid就是aa的pid 62658,
同理aaa,aab 也繼承了aa的$i值,這時$i的值為1, 當繼續循環時, $i 就變成了2, 也就只能循環一次了,相應aaa,aab 創建了子進程文件fork_child_process_2_62659_62663_82(aaaa),fork_child_process_2_62660_62664_59(aaba),而他們相應的父進程就是aaa(62659)和aab(62660).
至此, for循環中的多進程邏輯完成了, 也就是為何產生了第一部分的7個文件
=====================華麗的分割線=============================
而至于為何第二部分是8個文件, 各位可以自己思考一下, 注意, 無論主進程還是子進程, 在for循環完畢之后會繼續往下走, 知道這一點就好理解了。
在實際的代碼中, 我就犯了這種錯誤。
那如何解決上面的問題呢, 只要在子進程執行的最后exit就好啦,
fork_child_process_0_63219_63221_66 fork_child_process_1_63219_63222_88 fork_child_process_2_63219_63223_22 fork_process_pid_62225_63219_77
繼續,那么在網上看到很多多進程編程中使用pcntl_waitpid, 并不了解他是做什么的,且相應的例子很少, 我暫且來說說我的理解
pcntl_waitpid等待或返回fork的子進程狀態。
多進程的主進程創建了子進程,那主進程如何確認子進程的狀態呢。 假如主進程需要根據子進程的狀態做不同的處理呢, 這里的狀態包括子進程被kill掉,或變成僵尸進程等。 pcntl_waitpid就可以獲取子進程的狀態碼, 通過這個狀態碼, 就可知道子進程處于什么狀態
他的用法:
int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
返回的值可以是-1,0或者 >0的值, 如果是-1, 表示子進程出錯, 如果>0表示子進程已經退出且值是退出的子進程pid,至于如何退出, 可以通過$status狀態碼反應。 那什么時候返回0呢, 只有在option 參數為 WNOHANG且子進程正在運行時0, 也就是說當設置了options=WNOHANG時, 如果子進程還沒有退出, 此時pcntl_waitpid就會返回0
另外, 如果不設置這個參數為WNOHANG, pcntl_waitpid 就會阻塞運行, 直到子進程退出, 至于option的另外一個值WUNTRACED, 暫未理解, 不表
那么如何根據$status(狀態碼)判斷進程是如何退出呢, 如下(參數都是$status)
pcntl_wifexited這個函數可以根據$status 判斷進程是否正常退出, 何為正常退出, 比如exit
pcntl_wexitstatus這個函數僅在pcntl_wifexited 返回True(即正常退出)時有效, 且返回子進程退出的返回狀態碼, 這個返回狀態碼可以通過exit($s)的參數($s必須為整數時)定義
pcntl_wifsignaled檢查子進程狀態碼是否代表由于某個信號而中斷, 比如是不是我們給他發送了term, int 等信號了
pcntl_wexitstatus假如是發送信號而導致子進程中斷, 那么這個信號是什么信號呢, 這個函數就是獲取這個信號的
pcntl_wifstopped僅當option選項為WUNTRACED時有效, 未理解, 不表
pcntl_wtermsig同上
綜合實例代碼:
$res = pcntl_waitpid($pid, $status, WNOHANG); //FileLog::log("pid is $pid; wait result is $res"); if($res == -1 || $res > 0){ if(!pcntl_wifexited($status)){ //進程非正常退出 FileLog::log("service stop unusally; pid is $pid"); }else{ //獲取進程終端的退出狀態碼; $code = pcntl_wexitstatus($status); FileLog::log("service stop code: $code;pid is $pid "); } if(pcntl_wifsignaled($status)){ //不是通過接受信號中斷 FileLog::log("service stop not by signal;pid is $pid "); }else{ $signal = pcntl_wtermsig($status); FileLog::log("service stop by signal $signal;pid is $pid"); } }
上面的這個代碼就通過根據pcntl_waitpid的返回結果和狀態碼對子進程因為不同原因中斷做了不同的處理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21087.html
摘要:多進程中與多進程相關的兩個重要拓展是和。函數執行期間,主進程除了等待無法處理其他任務,所以一般不認為這是多進程編程。回收子進程有兩種方式,一種是主進程調用函數等待子進程結束另外一種是處理信號。 轉載請注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎 web請求 cookie web響應 session 數據庫操作 加解...
摘要:說明函數創建一個子進程,這個子進程僅進程號和父進程號與其父進程不同。返回值成功時,在父進程執行線程內返回產生的子進程的,在子進程執行線程內返回。失敗時,在父進程上下文返回,不會創建子進程,并且會引發一個錯誤。 pcntl 簡介 PHP的進程控制支持實現了Unix方式的進程創建, 程序執行, 信號處理以及進程的中斷。 進程控制不能被應用在Web服務器環境,當其被用于Web服務環境時可能會...
摘要:職場多年下來,技術也算是逐漸地有些積累,但是更重要的是對自身有了更加合理的人生定位。或許,人生的意義,就在于此處的感悟吧。基于的并發處理封裝類。對語言底層擴展的的深度解讀和生產應用。函數官網手冊中對的說明,更細化的需求可以研究深化。 個人聲明 作者:于立(wx/yulichenr) 敬告:聯系我,請注明來源和來意 本人開發有很多年了,但是很少整理分享,如今趁著清閑就為大家服務了,希...
摘要:第一次子進程正在休眠中,父進程依舊在循環中。第三次此時父進程已經執行了,將已經退出的子進程回收,釋放了等資源。梳理一下流程,子進程向父進程發送信號是對人們來說是透明的,也就是說我們無須關心。 [原文地址:https://blog.ti-node.com/blog...] 上一篇尬聊了通篇的pcntl_wait()和pcntl_waitpid(),就是為了解決僵尸進程的問題,但最后看起來...
閱讀 2106·2021-11-24 09:39
閱讀 1494·2019-08-30 15:44
閱讀 1946·2019-08-29 17:06
閱讀 3392·2019-08-29 16:32
閱讀 3543·2019-08-29 16:26
閱讀 2654·2019-08-29 15:35
閱讀 3025·2019-08-29 12:50
閱讀 1635·2019-08-29 11:15