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