具體細節過程重生和信號處理在PHP
我在PHP中,一個問題,當重生過程不處理的信號,而在此之前重生,裝卸工作正常。我縮小我的代碼非常簡單:
declare(ticks=1);
register_shutdown_function(function() {
if ($noRethrow = ob_get_contents()) {
ob_end_clean();
exit;
}
system('/usr/bin/nohup /usr/bin/php '.__FILE__. ' 1>/dev/null 2>/dev/null &');
});
function handler($signal)
{
switch ($signal) {
case SIGTERM:
file_put_contents(__FILE__.'.log', sprintf('Terminated [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
ob_start();
echo($signal);
exit;
case SIGCONT:
file_put_contents(__FILE__.'.log', sprintf('Restarted [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
exit;
}
}
pcntl_signal(SIGTERM, 'handler');
pcntl_signal(SIGCONT, 'handler');
while(1) {
if (time() % 5 == 0) {
file_put_contents(__FILE__.'.log', sprintf('Idle [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
}
sleep(1);
}
正如你所看到的,它下面:
- 註冊關機功能,在重生與
nohup
(這樣的過程,忽略SIGHUP
時父母進程死亡) - 註冊處理程序通過
pcntl_signal()
爲SIGTERM
和SIGCONT
。首先只會記錄一個消息,表明進程已終止,而第二會導致進程重新生成。它是通過ob_*
函數實現的,所以要傳遞一個標誌,關閉函數應該做什麼 - 退出或重新生成。 - 記錄一些腳本爲「活動」的信息以記錄文件。
發生了什麼
於是,我開始腳本:
/usr/bin/nohup /usr/bin/php script.php 1>/dev/null 2>/dev/null &
然後,在日誌文件中,也有類似的條目:
Idle [ppid=7171] [pid=8849]
Idle [ppid=7171] [pid=8849]
讓我們說,然後我做kill 8849
:
Terminated [ppid=7171] [pid=8849]
因此,它成功處理了SIGTERM
(而且腳本確實退出)。現在,如果我不是做kill -18 8849
,然後我看到(圖18是SIGCONT
數值):和
Idle [ppid=7171] [pid=8849]
Restarted [ppid=7171] [pid=8849]
Idle [ppid=1] [pid=8875]
Idle [ppid=1] [pid=8875]
,因此:第一,SIGCONT
也被正確處理,並通過接下來的「空閒」的消息來看,新生成的腳本實例運行良好。
更新#1:我在想的東西用ppid=1
(因此,init
全球進程)和孤立進程的信號處理,但它並非如此。這是log part,這表明孤兒(ppid=1
)進程不是原因:當工作人員通過控制應用程序啓動時,它也通過system()
命令調用它 - 與工作人員重新生成本身一樣。但是,在控制應用程序調用worker之後,它具有ppid=1
並正確響應信號,而如果worker自己重新生成,則新副本不響應,除SIGKILL
之外。所以,只有當工人重新出現時纔會出現問題。
更新#2:我試圖分析strace
發生了什麼。現在,這裏有兩個街區。
- 如果是尚未重生工人 - strace output。看看行
4
和5
,這是我發送SIGCONT
,因此kill -18
一個進程。然後觸發所有鏈:寫入文件,system()
調用並退出當前進程。 當工人已經自己重生 - strace output。在這裏,看看行
8
和9
- 他們出現後收到SIGCONT
。第一個:看起來過程仍然以某種方式接收信號,其次,它忽略了信號。沒有采取任何行動,但系統通知SIGCONT
已發送進程。那麼爲什麼這個過程忽略它 - 是一個問題(因爲如果SIGCONT
的用戶處理程序的安裝失敗,那麼它應該結束執行,而過程沒有結束)。至於SIGKILL
,那麼對於已經重生工人的產出是這樣的:nanosleep({1, 0}, <unfinished ...> +++ killed by SIGKILL +++
這表明,該信號被接收,並做了它應該做的。
問題
由於過程是重生,它不反應既不SIGTERM
,也不SIGCONT
。但是,仍然可以用SIGKILL
來結束它(所以,kill -9 PID
確實結束了該過程)。例如,對於上述過程,kill 8875
和kill -18 8875
都不會執行任何操作(進程將忽略信號並繼續記錄消息)。然而,我不會說註冊信號完全失敗 - 因爲它重新定義了至少SIGTERM
(通常導致終止,而在這種情況下它被忽略)。但是,我不會說註冊信號完全失敗 - 因爲它重新定義了至少SIGTERM
(通常導致終止,而在這種情況下它被忽略)。我還懷疑ppid = 1
指出了一些錯誤的東西,但我現在無法確定。
而且,我想任何其他種類的信號(實際上,這並不重要,什麼是信號代碼,結果總是相同的)
問題
可能是什麼這種行爲的原因?我正在重建一個過程的方式是否正確?如果不是,那麼其他哪些選項將允許新產生的進程正確使用用戶定義的信號處理程序?
我在想這個東西,但事實並非如此。這裏是[log part](http://pastebin.com/mSHL0VZu),它表明孤兒('ppid = 1')進程不是原因:當worker通過控制app啓動時,它也會用' system()'命令 - 就像工人重新生成自己一樣。但是,在控制應用程序調用worker之後,它具有'ppid = 1'並正確響應信號,而如果工作人員自己重新生成,新的副本不會響應它們,除了'SIGKILL'。所以,只有當**工人重新生成**時纔會出現問題。 – 2015-03-16 10:47:46
我已經做了一些使用中間shell腳本的測試,它就像你說的:'ppid = 1'不是問題,因爲第一次產生,第二次沒有。現在我沒有更多的想法。 – leafnode 2015-03-16 10:56:15
無論如何,我加了一些分析過程在做什麼。 – 2015-03-16 12:17:07