摘要:第一次子進程正在休眠中,父進程依舊在循環(huán)中。第三次此時父進程已經(jīng)執(zhí)行了,將已經(jīng)退出的子進程回收,釋放了等資源。梳理一下流程,子進程向父進程發(fā)送信號是對人們來說是透明的,也就是說我們無須關(guān)心。
[原文地址:https://blog.ti-node.com/blog...]
上一篇尬聊了通篇的pcntl_wait()和pcntl_waitpid(),就是為了解決僵尸進程的問題,但最后看起來還是有一些遺留問題,而且因為嘴欠在上篇文章的結(jié)尾出也給了解決方案:信號。
信號是一種軟件中斷,也是一種非常典型的異步事件處理方式。在NIX系統(tǒng)誕生的混沌之初,信號的定義是比較混亂的,而且最關(guān)鍵是不可靠,這是一個很嚴重的問題。所以在后來的POSIX標準中,對信號做了標準化同時也各個發(fā)行版的NIX也都提供大量可靠的信號。每種信號都有自己的名字,大概如SIGTERM、SIGHUP、SIGCHLD等等,在*NIX中,這些信號本質(zhì)上都是整形數(shù)字(游有心情的可以參觀一下signal.h系列頭文件)。
信號的產(chǎn)生是有多種方式的,下面是常見的幾種:
鍵盤上按某些組合鍵,比如Ctrl+C或者Ctrl+D等,會產(chǎn)生SIGINT信號。
使用posix kill調(diào)用,可以向某個進程發(fā)送指定的信號。
遠程ssh終端情況下,如果你在服務(wù)器上執(zhí)行了一個阻塞的腳本,正在阻塞過程中你關(guān)閉了終端,可能就會產(chǎn)生SIGHUP信號。
硬件也會產(chǎn)生信號,比如OOM了或者遇到除0這種情況,硬件也會向進程發(fā)送特定信號。
而進程在收到信號后,可以有如下三種響應(yīng):
直接忽略,不做任何反映。就是俗稱的完全不鳥。但是有兩種信號,永遠不會被忽略,一個是SIGSTOP,另一個是SIGKILL,因為這兩個進程提供了向內(nèi)核最后的可靠的結(jié)束進程的辦法。
捕捉信號并作出相應(yīng)的一些反應(yīng),具體響應(yīng)什么可以由用戶自己通過程序自定義。
系統(tǒng)默認響應(yīng)。大多數(shù)進程在遇到信號后,如果用戶也沒有自定義響應(yīng),那么就會采取系統(tǒng)默認響應(yīng),大多數(shù)的系統(tǒng)默認響應(yīng)就是終止進程。
用人話來表達,就是說假如你是一個進程,你正在干活,突然施工隊的喇叭里沖你嚷了一句:“吃飯了!”,于是你就放下手里的活兒去吃飯。你正在干活,突然施工隊的喇叭里沖你嚷了一句:“發(fā)工資了!”,于是你就放下手里的活兒去領(lǐng)工資。你正在干活,突然施工隊的喇叭里沖你嚷了一句:“有人找你!”,于是你就放下手里的活兒去看看是誰找你什么事情。當然了,你很任性,那是完全可以不鳥喇叭里喊什么內(nèi)容,也就是忽略信號。也可以更任性,當喇叭里沖你嚷“吃飯”的時候,你去就不去吃飯,你去睡覺,這些都可以由你來。而你在干活過程中,從來不會因為要等某個信號就不干活了一直等信號,而是信號隨時隨地都可能會來,而你只需要在這個時候作出相應(yīng)的回應(yīng)即可,所以說,信號是一種軟件中斷,也是一種異步的處理事件的方式。
回到上文所說的問題,就是子進程在結(jié)束前,父進程就已經(jīng)先調(diào)用了pcntl_waitpid(),導(dǎo)致子進程在結(jié)束后依然變成了僵尸進程。實際上在父進程不斷while循環(huán)調(diào)用pcntl_waitpid()是個解決辦法,大概代碼如下:
$pid = pcntl_fork(); if( 0 > $pid ){ exit("fork error.".PHP_EOL); } else if( 0 < $pid ) { // 在父進程中 cli_set_process_title("php father process"); // 父進程不斷while循環(huán),去反復(fù)執(zhí)行pcntl_waitpid(),從而試圖解決已經(jīng)退出的子進程 while( true ){ sleep( 1 ); pcntl_waitpid( $pid, &$status, WNOHANG ); } } else if( 0 == $pid ) { // 在子進程中 // 子進程休眠3秒鐘后直接退出 cli_set_process_title("php child process"); sleep( 20 ); exit; }
下圖是運行結(jié)果:
第一次:子進程正在休眠中,父進程依舊在循環(huán)中。
第二次:子進程已經(jīng)退出了,父進程依舊在循環(huán)中,但是代碼還沒有執(zhí)行到pcntl_waitpid(),所以在子進程退出后到父進程執(zhí)行回收前這段空隙內(nèi)子進程變成了僵尸進程。
第三次:此時父進程已經(jīng)執(zhí)行了pcntl_waitpid(),將已經(jīng)退出的子進程回收,釋放了pid等資源。
但是這樣的代碼有一個缺陷,實際上就是子進程已經(jīng)退出的情況下,主進程還在不斷while pcntl_waitpid()去回收子進程,這是一件很奇怪的事情,并不符合社會主義主流價值觀,不低碳不節(jié)能,代碼也不優(yōu)雅,不好看。所以,應(yīng)該考慮用更好的方式來實現(xiàn)。那么,我們篇頭提了許久的信號終于概要出場了。
現(xiàn)在讓我們考慮一下,為何信號可以解決“不低碳不節(jié)能,代碼也不優(yōu)雅,不好看”的問題。子進程在退出的時候,會向父進程發(fā)送一個信號,叫做SIGCHLD,那么父進程一旦收到了這個信號,就可以作出相應(yīng)的回收動作,也就是執(zhí)行pcntl_waitpid(),從而解決掉僵尸進程,而且還顯得我們代碼優(yōu)雅好看節(jié)能環(huán)保。
梳理一下流程,子進程向父進程發(fā)送SIGCHLD信號是對人們來說是透明的,也就是說我們無須關(guān)心。但是,我們需要給父進程安裝一個響應(yīng)SIGCHLD信號的處理器,除此之外,還需要讓這些信號處理器運行起來,安裝上了不運行是一件尷尬的事情。那么,在php里給進程安裝信號處理器使用的函數(shù)是pcntl_signal(),讓信號處理器跑起來的函數(shù)是pcntl_signal_dispatch()。
pcntl_signal(),安裝一個信號處理器,具體說明是pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ),參數(shù)signo就是信號,callback則是響應(yīng)該信號的代碼段,返回bool值。
pcntl_signal_dispatch(),調(diào)用每個等待信號通過pcntl_signal() 安裝的處理器,參數(shù)為void,返回bool值。
下面結(jié)合新引入的兩個函數(shù)來解決一下樓上的丑陋代碼:
$pid = pcntl_fork(); if( 0 > $pid ){ exit("fork error.".PHP_EOL); } else if( 0 < $pid ) { // 在父進程中 // 給父進程安裝一個SIGCHLD信號處理器 pcntl_signal( SIGCHLD, function() use( $pid ) { echo "收到子進程退出".PHP_EOL; pcntl_waitpid( $pid, $status, WNOHANG ); } ); cli_set_process_title("php father process"); // 父進程不斷while循環(huán),去反復(fù)執(zhí)行pcntl_waitpid(),從而試圖解決已經(jīng)退出的子進程 while( true ){ sleep( 1 ); // 注釋掉原來老掉牙的代碼,轉(zhuǎn)而使用pcntl_signal_dispatch() //pcntl_waitpid( $pid, &$status, WNOHANG ); pcntl_signal_dispatch(); } } else if( 0 == $pid ) { // 在子進程中 // 子進程休眠3秒鐘后直接退出 cli_set_process_title("php child process"); sleep( 20 ); exit; }
運行結(jié)果如下:
[原文地址:https://blog.ti-node.com/blog...]
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/29328.html
摘要:主進程退出子進程繼續(xù)執(zhí)行給進程重新起個名字加入我們出個子進程就可以搞定這些任務(wù),那么出個子進程,同時父進程要負責這個子進程的狀態(tài)等。 [原文地址:https://blog.ti-node.com/blog...] 干巴巴地叨逼叨了這么久,時候表演真正的技術(shù)了! 做個高端點兒的玩意吧,加入我們要做一個任務(wù)系統(tǒng),這個系統(tǒng)可以在后臺幫我們完成一大波(注意是一大波)數(shù)據(jù)的處理,那么我們自然想到...
摘要:多進程通信之一命名管道。多進程通信之三信號量與共享內(nèi)存。共享內(nèi)存是最快是進程間通信方式,因為個進程之間并不需要數(shù)據(jù)復(fù)制,而是直接操控同一份數(shù)據(jù)。的一些書籍中甚至不建議新手輕易使用這種進程間通信的方式,因為這是一種極易產(chǎn)生死鎖的解決方案。 [原文地址:https://blog.ti-node.com/blog...] 往往開啟多進程的目的是為了一起干活加速效率,前面說了不同進程之間的內(nèi)存...
閱讀 3256·2021-09-22 15:58
閱讀 1720·2019-08-30 14:17
閱讀 1722·2019-08-28 18:05
閱讀 1508·2019-08-26 13:33
閱讀 688·2019-08-26 12:20
閱讀 611·2019-08-26 12:18
閱讀 3195·2019-08-26 11:59
閱讀 1408·2019-08-26 10:36