摘要:孤兒進(jìn)程是指父進(jìn)程在出子進(jìn)程后,自己先完了。這個(gè)問(wèn)題很尷尬,因?yàn)樽舆M(jìn)程從此變得無(wú)依無(wú)靠無(wú)家可歸,變成了孤兒。在中,父進(jìn)程對(duì)子進(jìn)程的狀態(tài)收集等是通過(guò)和等完成的。這個(gè)函數(shù)返回退出的子進(jìn)程的進(jìn)程或者失敗返回。
[原文地址:https://blog.ti-node.com/blog...]
實(shí)際上,你們一定要記住:PHP的多進(jìn)程是非常值得應(yīng)用于生產(chǎn)環(huán)境具備高價(jià)值的生產(chǎn)力工具。
但我認(rèn)為在正式開(kāi)始吹牛之前還是要說(shuō)兩個(gè)基本概念:孤兒進(jìn)程、僵尸進(jìn)程。
上篇我整篇尬聊的都是pcntl_fork(),只管fork生產(chǎn),不管產(chǎn)后護(hù)理,實(shí)際上這樣并不符合主流價(jià)值觀,而且,操作系統(tǒng)本身資源有限,這樣無(wú)限生產(chǎn)不顧護(hù)理,操作系統(tǒng)也會(huì)吃不消的。
孤兒進(jìn)程是指父進(jìn)程在fork出子進(jìn)程后,自己先完了。這個(gè)問(wèn)題很尷尬,因?yàn)樽舆M(jìn)程從此變得無(wú)依無(wú)靠、無(wú)家可歸,變成了孤兒。用術(shù)語(yǔ)來(lái)表達(dá)就是,父進(jìn)程在子進(jìn)程結(jié)束之前提前退出,這些子進(jìn)程將由init(進(jìn)程ID為1)進(jìn)程收養(yǎng)并完成對(duì)其各種數(shù)據(jù)狀態(tài)的收集。init進(jìn)程是Linux系統(tǒng)下的奇怪進(jìn)程,這個(gè)進(jìn)程是以普通用戶(hù)權(quán)限運(yùn)行但卻具備超級(jí)權(quán)限的進(jìn)程,簡(jiǎn)單地說(shuō),這個(gè)進(jìn)程在Linux系統(tǒng)啟動(dòng)的時(shí)候做初始化工作,比如運(yùn)行g(shù)etty、比如會(huì)根據(jù)/etc/inittab中設(shè)置的運(yùn)行等級(jí)初始化系統(tǒng)等等,當(dāng)然了,還有一個(gè)作用就是如上所說(shuō)的:收養(yǎng)孤兒進(jìn)程。
僵尸進(jìn)程是指父進(jìn)程在fork出子進(jìn)程,而后子進(jìn)程在結(jié)束后,父進(jìn)程并沒(méi)有調(diào)用wait或者waitpid等完成對(duì)其清理善后工作,導(dǎo)致改子進(jìn)程進(jìn)程ID、文件描述符等依然保留在系統(tǒng)中,極大浪費(fèi)了系統(tǒng)資源。所以,僵尸進(jìn)程是對(duì)系統(tǒng)有危害的,而孤兒進(jìn)程則相對(duì)來(lái)說(shuō)沒(méi)那么嚴(yán)重。在Linux系統(tǒng)中,我們可以通過(guò)ps -aux來(lái)查看進(jìn)程,如果有[Z+]標(biāo)記就是僵尸進(jìn)程。
在PHP中,父進(jìn)程對(duì)子進(jìn)程的狀態(tài)收集等是通過(guò)pcntl_wait()和pcntl_waitpid()等完成的。依然還是要通過(guò)代碼還演示說(shuō)明:
演示并說(shuō)明孤兒進(jìn)程的出現(xiàn),并演示孤兒進(jìn)程被init進(jìn)程收養(yǎng):
0 ){ // 顯示父進(jìn)程的進(jìn)程ID,這個(gè)函數(shù)可以是getmypid(),也可以用posix_getpid() echo "Father PID:".getmypid().PHP_EOL; // 讓父進(jìn)程停止兩秒鐘,在這兩秒內(nèi),子進(jìn)程的父進(jìn)程ID還是這個(gè)父進(jìn)程 sleep( 2 ); } else if( 0 == $pid ) { // 讓子進(jìn)程循環(huán)10次,每次睡眠1s,然后每秒鐘獲取一次子進(jìn)程的父進(jìn)程進(jìn)程ID for( $i = 1; $i <= 10; $i++ ){ sleep( 1 ); // posix_getppid()函數(shù)的作用就是獲取當(dāng)前進(jìn)程的父進(jìn)程進(jìn)程ID echo posix_getppid().PHP_EOL; } } else { echo "fork error.".PHP_EOL; }
運(yùn)行結(jié)果如下圖:
可以看到,前兩秒內(nèi),子進(jìn)程的父進(jìn)程進(jìn)程ID為4129,但是從第三秒開(kāi)始,由于父進(jìn)程已經(jīng)提前退出了,子進(jìn)程變成孤兒進(jìn)程,所以init進(jìn)程收養(yǎng)了子進(jìn)程,所以子進(jìn)程的父進(jìn)程進(jìn)程ID變成了1。
演示并說(shuō)明僵尸進(jìn)程的出現(xiàn),并演示僵尸進(jìn)程的危害:
0 ){ // 下面這個(gè)函數(shù)可以更改php進(jìn)程的名稱(chēng) cli_set_process_title("php father process"); // 讓主進(jìn)程休息60秒鐘 sleep(60); } else if( 0 == $pid ) { cli_set_process_title("php child process"); // 讓子進(jìn)程休息10秒鐘,但是進(jìn)程結(jié)束后,父進(jìn)程不對(duì)子進(jìn)程做任何處理工作,這樣這個(gè)子進(jìn)程就會(huì)變成僵尸進(jìn)程 sleep(10); } else { exit("fork error.".PHP_EOL); }
運(yùn)行結(jié)果如下圖:
通過(guò)執(zhí)行ps -aux命令可以看到,當(dāng)程序在前十秒內(nèi)運(yùn)行的時(shí)候,php child process的狀態(tài)列為[S+],然而在十秒鐘過(guò)后,這個(gè)狀態(tài)變成了[Z+],也就是變成了危害系統(tǒng)的僵尸進(jìn)程。
那么,問(wèn)題來(lái)了?如何避免僵尸進(jìn)程呢?PHP通過(guò)pcntl_wait()和pcntl_waitpid()兩個(gè)函數(shù)來(lái)幫我們解決這個(gè)問(wèn)題。了解Linux系統(tǒng)編程的應(yīng)該知道,看名字就知道這其實(shí)就是PHP把C語(yǔ)言中的wait()和waitpid()包裝了一下。
通過(guò)代碼演示pcntl_wait()來(lái)避免僵尸進(jìn)程,在開(kāi)始之前先簡(jiǎn)單普及一下pcntl_wait()的相關(guān)內(nèi)容:這個(gè)函數(shù)的作用就是 “ 等待或者返回子進(jìn)程的狀態(tài) ”,當(dāng)父進(jìn)程執(zhí)行了該函數(shù)后,就會(huì)阻塞掛起等待子進(jìn)程的狀態(tài)一直等到子進(jìn)程已經(jīng)由于某種原因退出或者終止。換句話(huà)說(shuō)就是如果子進(jìn)程還沒(méi)結(jié)束,那么父進(jìn)程就會(huì)一直等等等,如果子進(jìn)程已經(jīng)結(jié)束,那么父進(jìn)程就會(huì)立刻得到子進(jìn)程狀態(tài)。這個(gè)函數(shù)返回退出的子進(jìn)程的進(jìn)程ID或者失敗返回-1。
0 ){ // 下面這個(gè)函數(shù)可以更改php進(jìn)程的名稱(chēng) cli_set_process_title("php father process"); // 返回$wait_result,就是子進(jìn)程的進(jìn)程號(hào),如果子進(jìn)程已經(jīng)是僵尸進(jìn)程則為0 // 子進(jìn)程狀態(tài)則保存在了$status參數(shù)中,可以通過(guò)pcntl_wexitstatus()等一系列函數(shù)來(lái)查看$status的狀態(tài)信息是什么 $wait_result = pcntl_wait( $status ); print_r( $wait_result ); print_r( $status ); // 讓主進(jìn)程休息60秒鐘 sleep(60); } else if( 0 == $pid ) { cli_set_process_title("php child process"); // 讓子進(jìn)程休息10秒鐘,但是進(jìn)程結(jié)束后,父進(jìn)程不對(duì)子進(jìn)程做任何處理工作,這樣這個(gè)子進(jìn)程就會(huì)變成僵尸進(jìn)程 sleep(10); } else { exit("fork error.".PHP_EOL); }
將文件保存為wait.php,然后php wait.php,在另外一個(gè)終端中通過(guò)ps -aux查看,可以看到在前十秒內(nèi),php child process是[S+]狀態(tài),然后十秒鐘過(guò)后進(jìn)程消失了,也就是被父進(jìn)程回收了,沒(méi)有變成僵尸進(jìn)程。
但是,pcntl_wait()有個(gè)很大的問(wèn)題,就是阻塞。父進(jìn)程只能掛起等待子進(jìn)程結(jié)束或終止,在此期間父進(jìn)程什么都不能做,這并不符合多快好省原則,所以pcntl_waitpid()閃亮登場(chǎng)。pcntl_waitpid( $pid, &$status, $option = 0 )的第三個(gè)參數(shù)如果設(shè)置為WNOHANG,那么父進(jìn)程不會(huì)阻塞一直等待到有子進(jìn)程退出或終止,否則將會(huì)和pcntl_wait()的表現(xiàn)類(lèi)似。
修改第三個(gè)案例的代碼,但是,我們并不添加WNOHANG,演示說(shuō)明pcntl_waitpid()功能:
0 ){ // 下面這個(gè)函數(shù)可以更改php進(jìn)程的名稱(chēng) cli_set_process_title("php father process"); // 返回值保存在$wait_result中 // $pid參數(shù)表示 子進(jìn)程的進(jìn)程ID // 子進(jìn)程狀態(tài)則保存在了參數(shù)$status中 // 將第三個(gè)option參數(shù)設(shè)置為常量WNOHANG,則可以避免主進(jìn)程阻塞掛起,此處父進(jìn)程將立即返回繼續(xù)往下執(zhí)行剩下的代碼 $wait_result = pcntl_waitpid( $pid, $status ); var_dump( $wait_result ); var_dump( $status ); // 讓主進(jìn)程休息60秒鐘 sleep(60); } else if( 0 == $pid ) { cli_set_process_title("php child process"); // 讓子進(jìn)程休息10秒鐘,但是進(jìn)程結(jié)束后,父進(jìn)程不對(duì)子進(jìn)程做任何處理工作,這樣這個(gè)子進(jìn)程就會(huì)變成僵尸進(jìn)程 sleep(10); } else { exit("fork error.".PHP_EOL); }
下面是運(yùn)行結(jié)果,一個(gè)執(zhí)行php程序的終端窗口,另一個(gè)是ps -aux終端窗口。實(shí)際上可以看到主進(jìn)程是被阻塞的,一直到第十秒子進(jìn)程退出了,父進(jìn)程不再阻塞:
那么我們修改第四段代碼,添加第三個(gè)參數(shù)WNOHANG,代碼如下:
0 ){ // 下面這個(gè)函數(shù)可以更改php進(jìn)程的名稱(chēng) cli_set_process_title("php father process"); // 返回值保存在$wait_result中 // $pid參數(shù)表示 子進(jìn)程的進(jìn)程ID // 子進(jìn)程狀態(tài)則保存在了參數(shù)$status中 // 將第三個(gè)option參數(shù)設(shè)置為常量WNOHANG,則可以避免主進(jìn)程阻塞掛起,此處父進(jìn)程將立即返回繼續(xù)往下執(zhí)行剩下的代碼 $wait_result = pcntl_waitpid( $pid, $status, WNOHANG ); var_dump( $wait_result ); var_dump( $status ); echo "不阻塞,運(yùn)行到這里".PHP_EOL; // 讓主進(jìn)程休息60秒鐘 sleep(60); } else if( 0 == $pid ) { cli_set_process_title("php child process"); // 讓子進(jìn)程休息10秒鐘,但是進(jìn)程結(jié)束后,父進(jìn)程不對(duì)子進(jìn)程做任何處理工作,這樣這個(gè)子進(jìn)程就會(huì)變成僵尸進(jìn)程 sleep(10); } else { exit("fork error.".PHP_EOL); }
面是運(yùn)行結(jié)果,一個(gè)執(zhí)行php程序的終端窗口,另一個(gè)是ps -aux終端窗口。實(shí)際上可以看到主進(jìn)程是被阻塞的,一直到第十秒子進(jìn)程退出了,父進(jìn)程不再阻塞:
我們看到子進(jìn)程是睡眠了十秒鐘,而父進(jìn)程在執(zhí)行pcntl_waitpid()之前沒(méi)有任何睡眠且本身不再阻塞,所以,主進(jìn)程自己先執(zhí)行下去了,而子進(jìn)程在足足十秒鐘后才結(jié)束,進(jìn)程狀態(tài)自然無(wú)法得到回收。如果我們將代碼修改一下,就是在主進(jìn)程的pcntl_waitpid()前睡眠15秒鐘,這樣就可以回收子進(jìn)程了。但是即便這樣修改,細(xì)心想的話(huà)還是會(huì)有個(gè)問(wèn)題,那就是在子進(jìn)程結(jié)束后,在父進(jìn)程執(zhí)行pcntl_waitpid()回收前,有五秒鐘的時(shí)間差,在這個(gè)時(shí)間差內(nèi),php child process也將會(huì)是僵尸進(jìn)程。那么,pcntl_waitpid()如何正確使用?。窟@樣用,看起來(lái)畢竟不太科學(xué)。
那么,是時(shí)候引入信號(hào)學(xué)了!
[原文地址:https://blog.ti-node.com/blog...]
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/29329.html
摘要:多進(jìn)程中與多進(jìn)程相關(guān)的兩個(gè)重要拓展是和。函數(shù)執(zhí)行期間,主進(jìn)程除了等待無(wú)法處理其他任務(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ù)庫(kù)操作 加解...
摘要:守護(hù)進(jìn)程是在一類(lèi)脫離終端在后臺(tái)執(zhí)行的程序通常以結(jié)尾隨系統(tǒng)啟動(dòng)其父進(jìn)程通常是進(jìn)程一般要讓當(dāng)前程序以守護(hù)進(jìn)程形式運(yùn)行在命令后加并重定向輸出即可或者使用也可以這是直接運(yùn)行程序的方式如果是用具體語(yǔ)言代碼的形式來(lái)實(shí)現(xiàn)呢首先看一下守護(hù)進(jìn)程的實(shí)現(xiàn)方式創(chuàng)建 守護(hù)進(jìn)程是在一類(lèi)脫離終端在后臺(tái)執(zhí)行的程序, 通常以d結(jié)尾, 隨系統(tǒng)啟動(dòng), 其父進(jìn)程(ppid)通常是init進(jìn)程 一般要讓當(dāng)前程序以守護(hù)進(jìn)程形式運(yùn)...
摘要:運(yùn)行模式實(shí)現(xiàn)進(jìn)程前,需了解常見(jiàn)的的運(yùn)行模式通用網(wǎng)關(guān)接口模式模式命令行模式模塊模式作為服務(wù)器模塊而進(jìn)程則是使用命令行模式運(yùn)行的基本實(shí)現(xiàn)中提供了一個(gè)擴(kuò)展,可以利用操作系統(tǒng)的調(diào)用來(lái)實(shí)現(xiàn)多進(jìn)程。 應(yīng)用場(chǎng)景 一些耗時(shí)任務(wù): 大數(shù)據(jù)表分表后的統(tǒng)計(jì)信息功能 分批發(fā)送短信或郵件功能 其他可分目標(biāo)的任務(wù)功能(很多種) 所以我們就需要一個(gè)常駐內(nèi)存的任務(wù)管理工具,為了保證實(shí)時(shí)性,一方面我們讓它一直執(zhí)行任...
摘要:運(yùn)行模式實(shí)現(xiàn)進(jìn)程前,需了解常見(jiàn)的的運(yùn)行模式通用網(wǎng)關(guān)接口模式模式命令行模式模塊模式作為服務(wù)器模塊而進(jìn)程則是使用命令行模式運(yùn)行的基本實(shí)現(xiàn)中提供了一個(gè)擴(kuò)展,可以利用操作系統(tǒng)的調(diào)用來(lái)實(shí)現(xiàn)多進(jìn)程。 應(yīng)用場(chǎng)景 一些耗時(shí)任務(wù): 大數(shù)據(jù)表分表后的統(tǒng)計(jì)信息功能 分批發(fā)送短信或郵件功能 其他可分目標(biāo)的任務(wù)功能(很多種) 所以我們就需要一個(gè)常駐內(nèi)存的任務(wù)管理工具,為了保證實(shí)時(shí)性,一方面我們讓它一直執(zhí)行任...
閱讀 3482·2021-11-19 09:40
閱讀 1499·2021-10-13 09:41
閱讀 2673·2021-09-29 09:35
閱讀 2718·2021-09-23 11:21
閱讀 1711·2021-09-09 11:56
閱讀 838·2019-08-30 15:53
閱讀 852·2019-08-30 15:52
閱讀 605·2019-08-30 12:47