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 的範圍內, 預設優先權為$priority: 優先權值, -20 到20 的範圍內, 預設優先權為0. 值越小說明越優先.
$pid: 如果沒有指定, 指當前進程
$process_identifier: 意義同pcntl_getpriority 的$process_identifier.
設定成功返回TRUE, 呼叫失敗FALSE.
透過pcntl_signal() 安裝的即將發生的訊號的處理器.
呼叫成功返回TRUE, 失敗返回false.
php 5.3.3 加入
bool pcntl_signal(int $signo, callback $handler[, bool $restart_syscatl_signal(int $signo, callback $handler[, bool $restart_syscatl = 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() 所做的是同樣的事情, 不過dpcntl_sigtimedwait 多了兩個增強增強功能的參數$seconds 和$nanoseconds, 這樣就允許腳本的停留時間有一個上限而不是無限制等待.
$set: 需要等待的信號列表
$siginfo: 用來向調用者返回等待得到的信號的信息,訊息內容請見pcntl_sigwaitinfo
$seconds: 超時的秒數
$nanoseconds: 超時的奈秒數
成功後, pcntl_sigtimedwiat() 回傳訊號編號
int pcntl_sigwaitinfo(m $set[, array $目前腳本的執行, 直到接受到$set 中的某個訊號, 如果其中的一個訊號將要到達( 例如被pcntl_sigprocmask 鎖定) 那麼pcntl_sigwaitinfo 將會立刻回傳
$set: 例如的訊號清單
$siginfo: 用來向呼叫者回傳等待得到的訊號的資訊, 此資訊包含下列內容:
1. 所有訊號中資訊包含下列內容:
1. 所有訊號編號 資料包含下列內容:
1. 所有訊號中使用下列資料:
a) signo: 訊號編號
b) signo: c) code: 訊號代碼
2. SIGCHLD 訊號特有的資訊
a) stime: 系統消耗時間
d) pid: 傳送流程id
e ) uid: 發送流程的真實使用者id
3. SIGILL, SIGFPE, SIGSEGV, SIGBUS 擁有的資訊
a)
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: 如果你的系統允許你的系統允許標準的 optionit3(opwait3(options, options: optionit3(optionit3(optionit3(option) 類如果不提供這個參數, 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, ptltl_ws_sig. itstatus (int $status):
回傳一個已中斷的子程序回傳代碼, 此函數只在pcntl_wifexited 函數傳回TRUE 時有用.
$status 參數是pcntl_waitpid 產生的狀態資訊.
bool pcntl_wifexited(int $status):(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 時才有效.
更多PHP 使用pcntl和libevent 實現Timer功能相關文章請關注PHP中文網!