摘要:任何進程在退出前使用退出都會變成僵尸進程用于保存進程的狀態等信息,然后由進程接管。這時候就算手動結束腳本程序也無法關閉這個僵尸子進程了。那么子進程結束后,沒有回收,就產生僵尸進程了。本小節我們通過安裝信號處理函數來解決僵尸進程問題。
上一篇文章講解了pcntl_fork和pcntl_wait兩個函數的使用,本篇繼續講解PHP多進程相關新知識。
僵尸(zombie)進程這里說下僵尸進程:
僵尸進程是指的父進程已經退出,而該進程dead之后沒有進程接受,就成為僵尸進程(zombie)進程。任何進程在退出前(使用exit退出) 都會變成僵尸進程(用于保存進程的狀態等信息),然后由init進程接管。如果不及時回收僵尸進程,那么它在系統中就會占用一個進程表項,如果這種僵尸進程過多,最后系統就沒有可以用的進程表項,于是也無法再運行其它的程序。
通過如下命令查看是否有僵尸進程,如果有,類似下面這樣:
$ ps -A -o stat,ppid,pid,cmd | grep -e "^[Zz]" Z+ 282 283 [php]
如果子進程還沒有結束時,父進程就結束了,那么init進程會自動接手這個子進程,進行回收。
如果父進程是循環,又沒有安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束。那么子進程結束后,沒有回收,就產生僵尸進程了。
示例:
fork_zombie.php
命令行里運行程序,然后新終端查看:
$ ps -A -o stat,ppid,pid,cmd | grep -e "^[Zz]" Z+ 7252 7253 [php]出現了一個僵尸進程。這時候就算手動結束腳本程序也無法關閉這個僵尸子進程了。需要使用kill -9關閉。
pcntl_signalbool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )該函數為signo指定的信號安裝一個新的信號處理器。
安裝SIGCHLD信號上一節里,我們講到僵尸進程產生的原因:
如果父進程是循環,又沒有安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束。那么子進程結束后,沒有回收,就產生僵尸進程了。本小節我們通過安裝SIGCHLD信號處理函數來解決僵尸進程問題。示例:
第一次注釋掉#1和#2處的代碼,父進程提前結束,子進程被init進程接手,所以沒有產生僵尸進程。
第二次我們注釋掉#2處的代碼,開啟#1處的代碼,即父進程是個死循環,又沒有回收子進程,就產生僵尸進程了。
第三次我們開啟#1處和#2處的代碼,父進程由于安裝了信號處理,并調用wait函數等待子進程結束,所以也沒有產生僵尸進程。對子進程的結束不感興趣
如果父進程不關心子進程什么時候結束,那么可以用pcntl_signal(SIGCHLD, SIG_IGN)通知內核,自己對子進程的結束不感興趣,那么子進程結束后,內核會回收,并不再給父進程發送信號。這樣我們就不寫子進程退出的處理函數了。說明:
如果去掉declare( ticks = 1 );無法響應信號。因php的信號處理函數是基于ticks來實現的,而不是注冊到真正系統底層的信號處理函數中。安裝其他信號我們可以在主進程安裝更多信號,例如:
注:通過 kill -l 可以看到Linux下所有的信號常量。$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
防盜版聲明:本文系原創文章,發布于公眾號飛鴻影的博客(fhyblog)及博客園,轉載需作者同意。
ticks相關PHP的 ticks=1 表示每執行1行PHP代碼就回調此函數(指的pcntl_signal_dispatch)。實際上大部分時間都沒有信號產生,但ticks的函數一直會執行。如果一個服務器程序1秒中接收1000次請求,平均每個請求要執行1000行PHP代碼。那么PHP的pcntl_signal,就帶來了額外的 1000 * 1000,也就是100萬次空的函數調用。這樣會浪費大量的CPU資源。
(摘自:韓天峰(Rango)的博客 ? PHP官方的pcntl_signal性能極差
http://rango.swoole.com/archi...pcntl_signal_dispatch的作用就是查看是否收到了信號需要處理,如果有信號的話,就調用相應的信號處理函數。
所以上述問題比較好的做法是去掉ticks,轉而手動調用pcntl_signal_dispatch,在代碼循環中自行處理信號。
我們把上一小節的例子改改,不使用ticks:
運行結果:
Installing signal handler... Generating signal SIGTERM to self... Caught SIGUSR1... Done相比每執行一條php語句都會調用 pcntl_signal_dispatch 一次,效率好多了。
pcntl_alarmint pcntl_alarm ( int $seconds )該函數創建一個計時器,在指定的秒數后向進程發送一個 SIGALRM 信號。每次對 pcntl_alarm() 的調用都會取消之前設置的alarm信號。注意不是定時器,只會運行一次。
下面是一個隔5秒發送一個SIGALRM信號,并由signal_handler函數獲取,然后打印一個 SIGALRM 的例子:
注:如果不想使用ticks,那么需要在主循環里主動增加pcntl_signal_dispatch()調用。(未完待續)
歡迎關注公眾號及時獲取最新文章推送!
推薦!每月僅需$2.5,即可擁有配置SSD的VPS!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28866.html
摘要:用于創建子進程。該函數阻塞當前進程,只到當前進程的一個子進程退出或者收到一個結束當前進程的信號。注意處需要注意子進程需要防止子進程也進入循環。如果沒有,最終創建的子進程不只個。 本系列文章將向大家講解pcntl_*系列函數,從而更深入的理解進程相關知識。 PCNTL在PHP中進程控制支持默認是關閉的。您需要使用 --enable-pcntl 配置選項重新編譯PHP的 CGI或CLI版本...
摘要:消息隊列更常見的用途是主進程分配任務,子進程消費執行。子進程前面加了個,這是為了防止父進程還未往消息隊列中加入內容直接退出。 前面幾節都是講解pcntl擴展實現的多進程程序。本節給大家介紹swoole擴展的swoole_process模塊。 swoole多進程 swoole_process 是swoole提供的進程管理模塊,用來替代PHP的pcntl擴展。 首先,確保安裝的swoole...
摘要:本節主要講解常用函數和進程池的概念,也會涉及到守護進程的知識。所以任何時候,建議預先創建好進程,也就是使用進程池的方式實現。 本節主要講解Posix常用函數和進程池的概念,也會涉及到守護進程的知識。本節難度較低。 Posix常用函數 posix_kill 向指定pid進程發送信號。成功時返回 TRUE , 或者在失敗時返回 FALSE 。 bool posix_kill ( int $...
摘要:本節講解幾個多進程的實例。新開終端,我們使用命令查看進程可以看到個進程個主進程,個子進程。使用命令結束子進程,主進程會重新拉起一個新的子進程。 本節講解幾個多進程的實例。 多進程實例 Master-Worker結構 下面例子實現了簡單的多進程管理: 支持設置最大子進程數 Master-Worker結構:Worker掛掉,Master進程會重新創建一個
摘要:進程切換太多,影響了了效率應該是原因之一。當時,十萬條記錄,個進程插入總時間為單進程插入萬條數據,耗時秒,相對個進程插入萬記錄來說,耗時少些。而單進程插入萬條記錄,耗時,相對來說,是挺慢的了。 個人在虛擬機centos7,單核,1G內存 /** * 模擬并發請求,10萬次寫入數據庫 * 拆分為10個進程,每個進程處理一萬條插入 */ $total = 10000; $num ...
閱讀 2665·2021-11-11 16:54
閱讀 3657·2021-08-16 10:46
閱讀 3441·2019-08-30 14:18
閱讀 3034·2019-08-30 14:01
閱讀 2723·2019-08-29 14:15
閱讀 2007·2019-08-29 11:31
閱讀 3083·2019-08-29 11:05
閱讀 2583·2019-08-26 11:54