PHP 使用pcntl和libevent 實現Timer功能,先看例子,pcntl(PHP線程)解釋在下面。
復制代碼 代碼如下:
<?php
function newChild($func_name) {
echo "enter newChild\n";
$args = func_get_args();
unset($args[0]);
$pid = pcntl_fork();
if ($pid == 0) {
function_exists($func_name) and exit(call_user_func_array($func_name, $args)) or exit(-1);
} else if($pid == -1) {
echo "Couldn't create child process";
} else {
return $pid;
}
}
(PS:^_^不錯的php開發交流群:256271784,驗證:csl,有興趣的話可以加入進來一起討論)
function on_timer() {
echo "timer called\n";
}
/**
* @param $func string, function name
* @param $timeouts int, microtimes for time delay
*/
function timer($func, $timeouts){
echo "enter timer\n";
$base = event_base_new();
$event = event_new();
event_set($event, 0, EV_TIMEOUT, $func);
event_base_set($event, $base);
event_add($event, $timeouts);
event_base_loop($base);
}
$pid = newChild("timer", "on_timer", 5000000);
if ($pid > 0) {
echo "master process exit\n";
}
PHP 擴展pcntl 實現 ” 多線程 ”( 進程 )
pcntl 與 ticks
ticks 是通過 declare(ticks = n) {statement} 語法定義的 , declare 語法目前只能接受 ticks, 他定義的 ticks = n 的意義是當 declare 指定的語句塊中執行了 N 條低級語句去發生一個事件 , 這個事件可以通過 register_tick_function($function_name) 來注冊 .
pcntl 的信號機制是基於 ticks 機制實現的 . 因此 , 我們使用 pcntl 族函數中信號相關的函數時 , 需要在前面增加 declare(ticks = n) 語法結構 .
int pcntl_alarm(int $seconds):
$seconds 秒後向進程發送一個 SIGALRM 信號 , 每次調用 pcntl_alarm 方法都會取消之前設置的時鐘 .
void pcntl_exec(string $path[, array $args[, array $env]]):
在當前進程空間執行一個程序 .
$path: 必須是二進制可執行文件 , 或具有有效腳本頭信息 (#!/usr/local/bin/php) 的腳本文件路徑 .
$args: 將要傳遞給該程序的字符串參數列表 ( 數組形式 )
$envs: 環境變量 . 以數組 (key => value 形式 ) 方式傳遞給要執行程序的環境變量 .
int pcntl_for k (void):
創建一個子進程 , 該子進程與父進程僅僅是 PID( 進程號 ) 和 PPID( 父進程號 ) 不同 .
在父線程執行時返回創建的子進程 pid, 在子線程執行時返回 0, 創建子進程失敗時會在父進程上下文返回 -1, 並引發 php 錯誤 .
理解這裡的 fork 需要知道 : pcntl_fork 創建的是一個分支節點 , 相當於一個標記 , 父進程完成後 , 子進程會從標記處繼續執行 , 也就是說 pcntl_fork 後面的代碼分別被父進程和子進程執行了兩遍 , 而兩個進程在執行過程中得到的返回值是不同的 . 因此 , 才可以分離父子進程執行不同的代碼 .
int pcntl_getpriority([int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]):
獲取給定 $pid 對應的進程的優先級 , 默認是通過 getmypid() 獲取到的值也就是當前進程 .
$pid: 如果沒有指定 , 默認是當前進程 .
$process_identifier: PRIO_PGRP, PRIO_USER, PRIO_PROCESS 三者之一 , 默認 PRIO_PROCESS. 其中 PRIO_PGRP 指獲取進程組的優先級 , PRIO_USER 指獲取用戶進程的優先級 , PRIO_PROCESS 指獲取特定進程優先級 .
返回進程的優先級 , 或者在發生錯誤時返回 false, 值越小說明越優先
bool pcntl_setpriority(int $priority[, int $pid = getmypid()[, int $process_identifier = PRIO_PROCESS]]:
設置進程的優先級 .
$priority: 優先級值 , -20 到 20 的范圍內 , 默認優先級為 0. 值越小說明越優先 .
$pid: 如果沒有指定 , 指當前進程
$process_identifier: 意義同 pcntl_getpriority 的 $process_identifier.
設置成功返回 TRUE, 失敗返回 FALSE.
bool pcntl_signal_dispatch(void):
調用通過 pcntl_signal() 安裝的即將發生的信號的處理器 .
調用成功返回 TRUE, 失敗返回 false.
php 5.3.3 加入
bool pcntl_signal(int $signo, callback $handler[, bool $restart_syscalls = true]):
為指定的信號 $signo 安裝一個新的信號處理器 $handler.
最後一個參數不明白意義 .
bool pcntl_sigprocmask(int $how, array $set[, array &$oldset]):
增加 , 刪除或設置鎖信號 , 具體的行為依賴於 $how 參數
$how: SIG_BLOCK 用於把信號增加到當前鎖信號中 , SIG_UNBLOCK 用於把信號從當前鎖信號中移除 , SIG_SETMASK 用於用給定的信號列表替換當前鎖信號 .
$set: 要增加 , 移除或設置的信號列表 .
$oldset: 用於向調用者返回舊的鎖定信號 .
成功返回 TRUE, 失敗返回 FALSE.
int pcntl_sigtimedwait(array $set[, array &$siginfo[, int $seconds = 0[, int $nanoseconds = 0]]]):
pcntl_sigtimedwait 實際上和 pcntl_sigwaitinfo() 所做的是同樣的事情 , 不過 pcntl_sigtimedwait 多了兩個增強的參數 $seconds 和 $nanoseconds, 這樣就允許腳本的停留時間有一個上限而不是無限制等待 .
$set: 需要等待的信號列表
$siginfo: 用來向調用者返回等待得到的信號的信息 , 信息內容見 pcntl_sigwaitinfo
$seconds: 超時的秒數
$nanoseconds: 超時的納秒數
成功後 , pcntl_sigtimedwiat() 返回信號編號
int pcntl_sigwaitinfo(array $set[, array &$siginfo]):
掛起當前腳本的執行 , 直到接受到 $set 中的某個信號 , 如果其中的一個信號將要到達 ( 比如被 pcntl_sigprocmask 鎖定 ) 那麼 pcntl_sigwaitinfo 將會立刻返回
$set: 等待的信號列表
$siginfo: 用來向調用者返回等待得到的信號的信息 , 該信息包含以下內容 :
1. 所有信號都有以下三個信息 :
a) signo: 信號編號
b) errno: 錯誤號
c) code: 信號代碼
2. SIGCHLD 信號特有的信息
a) status: 退出的值或信號
b) utime: 用戶消耗時間
c) stime: 系統消耗時間
d) pid: 發送進程 id
e) uid: 發送進程的真實用戶 id
3. SIGILL, SIGFPE, SIGSEGV, SIGBUS 擁有的信息
a) addr: 產生故障的內存位置
4. SIGPOLL 特有的信息 :
a) band: band event, 意義未知
b) fd: 文件描述符
函數成功運行返回信號編號
int pcntl_wait(int &$status[, int *options = 0]):
掛起當前進程直到一個子進程退出或直到一個信號要求終止當前進程或調用一個信號處理函數 . 如果子進程在調用時已經退出 ( 俗稱成為了僵屍進程 ), 此函數會馬上返回 , 所有的系統資源都將被釋放 .
$status 用來保存子進程的狀態信息 , 該狀態信息由以下函數產生 : pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.
$options: 如果你的系統允許 wait3( 大多數的 BSD 類系統 ), 你可以提供一個可選的 options 參數 , 如果不提供這個參數 , wait 將會使用系統調用 , 如果系統不允許 wait3, 提供這個參數不會有任何影響 , $options 的值可以是 0 或者 WNOHANG 和 WUNTRACED 兩個常數 .
函數返回退出的子進程的 PID, 或在錯誤時返回 -1, 或者如果提供 WNOHANG 作為 option(wait3 不可用的系統 ) 並且無有效子進程返回 0
僵屍進程 : 由於父進程在 fork 之後 , 無法預知子進程什麼時候結束 , 所以子進程為了要留給父進程一些信息 , 會留下一個稱作僵屍的數據結構 , 等待由父進程發起 wait 的操作來為它收屍 , 在子進程結束 ( 邏輯結束 ) 到父進程收屍前這一段時間子進程就被稱為僵屍進程 , 在父進程結束後 , 所有的子進程會交由 Init 來負責 , 因此 , 如果父進程結束 , 僵屍進程都會被回收 , 但是 , 如果父進程永遠不結束 , 這些僵屍進程就一直占用進程號 , 如果系統進程號耗盡 , 那麼將導致無法啟動新進程 , 因此 , 安全的做法是在父進程中為自己產生的子進程去收屍 .
int pcntl_waitpid(int $pid, int &$status[, int $options = 0]):
掛起當前進程直到給定 $pid 的子進程退出 , 或者當前進程接受到一個退出信號 , 或者接受到一 ige 信號去調用一個信號處理器 .
如果給定 $pid 對應的子進程在調用此函數時已經退出 ( 僵屍態 ), 函數立刻返回 , 所有的系統資源被釋放 .
$pid: 進程號 , 小於 -1 表明等待的是進程組中的任何子進程 , 進程組號就是 $pid 的絕對值 . 等於 -1 表明等待任意紫禁城 , 與 pcntl_wait 函數行為一致 . 等於 0 代表等待與調用進程在同一組的子進程 , 大於 0 代表是特定的進程 .
$status: 用來由函數返回子進程狀態 . 該狀態信息由以下函數產生 : pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig.
$options: 與 pcntl_wait 的 $options 意義相同
int pcntl_wexitstatus(int $status):
返回一個已經中斷的子進程返回代碼 , 此函數僅在 pcntl_wifexited 函數返回 TRUE 時有用 .
$status 參數是 pcntl_waitpid 產生的狀態信息 .
bool pcntl_wifexited(int $status):
檢查給定狀態是否表明子進程是正常退出的 .
bool pcntl_wifsignaled(int $status):
檢查給定狀態是否表明子進程是由於收到某個信號退出的 .
bool pcntl_wifstopped(int $status):
檢查 $status 是否能表明子進程當前已經停止 , 這個函數只有在作用於 pcntl_waitpid 函數使用的 WUNTRACED 作為 $options 參數的值時產生的 $status 上才有效 .
int pcntl_wstopsig(int $status):
通過分析 $status 返回使得子進程停止的信號的編號 , 這個函數只有在 pcntl_wifsignaled 返回 TRUE 時才有效 .
int pcntl_wtermsig(int $status):
返回使進程中斷的信號編號 . 這個函數只有在 pcntl_wifsignaled 返回 TRUE 時才有效 .