摘要:代碼實現啟動啟動流程見流程,主要包括守護進程保存注冊信號處理器創建多進程這部分。模擬調度實際用實現捕獲信號其中,會在每次調度過程中,捕獲信號并執行注冊的信號處理器。
首發于 樊浩柏科學院
經過 用 PHP 玩轉進程之一 — 基礎 的回顧復習,我們已經掌握了進程的基礎知識,現在可以嘗試用 PHP 做一些簡單的進程控制和管理,來加深我們對進程的理解。接下來,我將用多進程模型實現一個簡單的 PHPServer,基于它你可以做任何事。
PHPServer 完整的源代碼,可前往 fan-haobai/php-server 獲取。
總流程該 PHPServer 的 Master 和 Worker 進程主要控制流程,如下圖所示:
其中,主要涉及 3 個對象,分別為 入口腳本、Master 進程、Worker 進程。它們扮演的角色如下:
入口腳本:主要實現 PHPServer 的啟動、停止、重載功能,即觸發 Master 進程start、stop、reload流程;
Master 進程:負責創建并監控 Worker 進程。在啟動階段,會注冊信號處理器,然后創建 Worker;在運行階段,會持續監控 Worker 進程健康狀態,并接受來自入口腳本的控制信號并作出響應;在停止階段,會停止掉所有 Worker 進程;
Worker 進程:負責執行業務邏輯。在被 Master 進程創建后,就處于持續運行階段,會監聽到來自 Master 進程的信號,以實現自我的停止;
整個過程,又包括 4 個流程:
流程 ① :以守護態啟動 PHPServer 時的主要流程。入口腳本會進行 daemonize,也就是實現進程的守護態,此時會fork出一個 Master 進程;Master 進程先經過 保存 PID、注冊信號處理器 操作,然后 創建 Worker 會fork出多個 Worker 進程;
流程 ② :為 Master 進程持續監控的流程,過程中會捕獲入口腳本發送來的信號。主要監控 Worker 進程健康狀態,當 Worker 進程異常退出時,會嘗試創建新的 Worker 進程以維持 Worker 進程數量;
流程 ③ :為 Worker 進程持續運行的流程,過程中會捕獲 Master 進程發送來的信號。流程 ① 中 Worker 進程被創建后,就會持續執行業務邏輯,并阻塞于此;
流程 ④ :停止 PHPServer 的主要流程。入口腳本首先會向 Master 進程發送 SIGINT 信號,Master 進程捕獲到該信號后,會向所有的 Worker 進程轉發 SIGINT 信號(通知所有的 Worker 進程終止),等待所有 Worker 進程終止退出;
在流程 ② 中,Worker 進程被 Master 進程fork出來后,就會 持續運行 并阻塞于此,只有 Master 進程才會繼續后續的流程。代碼實現 啟動
啟動流程見 流程 ①,主要包括 守護進程、保存 PID、注冊信號處理器、創建多進程 Worker 這 4 部分。
守護進程首先,在入口腳本中fork一個子進程,然后該進程退出,并設置新的子進程為會話組長,此時的這個子進程就會脫離當前終端的控制。如下圖所示:
這里使用了 2 次fork,所以最后fork的一個子進程才是 Master 進程,其實一次fork也是可以的。代碼如下:
protected static function daemonize() { umask(0); $pid = pcntl_fork(); if (-1 === $pid) { exit("process fork fail "); } elseif ($pid > 0) { exit(0); } // 將當前進程提升為會話leader if (-1 === posix_setsid()) { exit("process setsid fail "); } // 再次fork以避免SVR4這種系統終端再一次獲取到進程控制 $pid = pcntl_fork(); if (-1 === $pid) { exit("process fork fail "); } elseif (0 !== $pid) { exit(0); } }
通常在啟動時增加-d參數,表示進程將運行于守護態模式。
當順利成為一個守護進程后,Master 進程已經脫離了終端控制,所以有必要關閉標準輸出和標準錯誤輸出。如下:
protected static function resetStdFd() { global $STDERR, $STDOUT; //重定向標準輸出和錯誤輸出 @fclose(STDOUT); fclose(STDERR); $STDOUT = fopen(static::$stdoutFile, "a"); $STDERR = fopen(static::$stdoutFile, "a"); }保存PID
為了實現 PHPServer 的重載或停止,我們需要將 Master 進程的 PID 保存于 PID 文件中,如php-server.pid文件。代碼如下:
protected static function saveMasterPid() { // 保存pid以實現重載和停止 static::$_masterPid = posix_getpid(); if (false === file_put_contents(static::$pidFile, static::$_masterPid)) { exit("can not save pid to" . static::$pidFile . " "); } echo "PHPServer start