1. 程式人生 > >Zend Signal in PHP 5.4

Zend Signal in PHP 5.4

在PHP5.4中, 根據由Rasmus提交的RFC, 引入了一套新的訊號處理機制, 目的是為了使得訊號遮蔽機制可以應用到任何SAPI中, 並且提高在這個過程中的PHP效能.

新的機制, 叫做zend signal, 它的理念, 來自Yahoo的”延遲訊號處理”(Yahoo signal deferring mechanism), 而後, facebook把這套理念加入了PHP中, 為了提升PHP+Apache 1.X下PHP呼叫ap_block/ap_unblock的效能.

在詳細介紹之前, 我想還是先介紹下引入這個新機制的背景:

之前我寫過倆篇blog, 介紹過因為超時訊號導致PHP crash的案例:

深入理解PHP記憶體管理之一個低概率Core的分析一個低概率的PHP Core dump , 在其中, 我說過, 其實PHP在關鍵操作的時候, 是預留了訊號遮蔽機制的:HANDLE_BLOCK和UNBLOCK_INTERRUPTIONS. 但是, 這倆個巨集只是Hook, 需要SAPI自己去實現, 目前來說, 也只有Apache 1.x的SAPI實現了這倆個巨集, 也就是使用ap_block和ap_unblock.

而對於”一個低概率的PHP Core dump “中所描述的情況, 如果我們為了解決它, 而在每次有錯誤發生的時刻, 都引入一對遮蔽/取消遮蔽的系統呼叫, 那麼這個效能損失將會很明顯, 所以一直沒有很好的解決這個問題.

那麼zend signal的做法是:

1. 在zend engine啟動時刻, 會為下面的訊號註冊訊號處理函式: SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2, SIGPROF(*nix下), 如果這些訊號已經有了處理函式, 那麼會把舊的處理函式儲存下來.

2. 當有訊號發生時候, zend_signal_handler_defer會首先判斷, 當前是否處於block區域, 如果不是, 則訊號對應的舊的處理函式將會被呼叫. 如果是, 那麼訊號處理函式不會被立即呼叫, 而是一直等到HANDLE_UNBLOCK_INTERRUPTIONS以後, 退出block區域, 才呼叫訊號處理函式. 如果有多個訊號發生, 則訊號將會排隊等候.

3. zend signal使用zend_signal_globals_t.depth計數, 來判斷是否處於block區域, HANDLE_BLOCK遞增, HANDLE_UNBLOCK_INTERRUPTIONS遞減.當 zend_signal_globals_t.depth大於0, 則表示在block中, 否則就表示不在. 這樣就保證了效能(避免以前呼叫sigaction來遮蔽訊號).

另外, zend signal為PHP提供了新的訊號處理註冊介面: zend_signal.

在zend signal引入的大背景下, 我終於解決了文章開頭所說的超時訊號可能導致crash的問題:#60038. (only in 5.4)

不過, 還是要提醒下: PHP 5.4還處於開發階段, 在最終release之前, 任何新特性都可能被調整或者更改. 如果大家有任何建議, 也歡迎反饋, 幫助我們使得PHP變得更好.

謝謝