PHP的pcntl擴展提供了信號處理的功能,利用它可以讓PHP來接管信號的處理,在開發服務器端守護進程方面,信號處理至關重要。
函數原型
bool pcntl_signal(int $signo ,callback $handler [,bool $restart_syscalls=true])
第一個參數是信號ID
第二個參數是信號發生時回調的PHP函數。
第三個參數是是否restart,是否重新注冊此信號。這個參數如果為false,那此信號只注冊處理一次。
pcntl_signal的實現
<?php
//信號處理需要注冊ticks才能生效,這里務必注意
//PHP5.4以上版本就不再依賴ticks了
declare(ticks = 1);
function sig_handler($signo){
switch ($signo) {
case SIGUSR1: echo "SIGUSR1\n"; break;
case SIGUSR2: echo "SIGUSR2\n"; break;
default: echo "unknow"; break;
}
}
//安裝信號觸發器器
pcntl_signal(SIGUSR1, "sig_handler");
pcntl_signal(SIGUSR2, "sig_handler");
//向當前進程發送SIGUSR1信號
posix_kill(posix_getpid(), SIGUSR1);
posix_kill(posix_getpid(), SIGUSR2);
?>
執行此代碼會在終端輸出你想要的結果,其實官方的pcntl_signal性能極差,主要是PHP的函數無法直接注冊到操作系統信號設置中,所以pcntl信號需要依賴tick機制來完成。
pcntl_signal的實現原理是,觸發信號后先將信號加入一個隊列中。然后在PHP的ticks回調函數中不斷檢查是否有信號,如果有信號就執行PHP中指定的回調函數,如果沒有則跳出函數。
ticks=1表示每執行1行PHP代碼就回調此函數。實際上大部分時間都沒有信號產生,但ticks的函數一直會執行。
比較好的做法是去掉ticks,轉而使用pcntl_signal_dispatch,在代碼循環中自行處理信號。
pcntl_signal_dispatch的實現
<?php
// 定義一個處理器,接收到SIGINT信號后只輸出一行信息
function signalHandler($signo) {
switch ($signo) {
case SIGUSR1: echo "SIGUSR1\n"; break;
case SIGUSR2: echo "SIGUSR2\n"; break;
default: echo "unknow"; break;
}
}
//安裝信號觸發器器
pcntl_signal(SIGINT, 'signalHandler');
while (true) {
sleep(1);
posix_kill(posix_getpid(), SIGUSR1);
pcntl_signal_dispatch(); //接收到信號時,調用注冊的signalHandler()
}
實戰:用信號來處理函數超時
<?php
function a(){
sleep(10);
echo "OK\n";
}
function b(){
echo "Stop\n";
}
function c(){
usleep(100000);
}
//信號處理代碼
function sig(){
throw new Exception;
}
try{
pcntl_alarm(2); //設定超時后觸發的信號
pcntl_signal(SIGALRM, "sig");
pcntl_signal_dispatch();
a();
pcntl_alarm(0);
}catch(Exception $e){
echo "timeout\n";
}
b();
a(); //等待十秒后完成
b();