成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

用PHP玩轉(zhuǎn)進(jìn)程之二 — 多進(jìn)程PHPServer

stormjun / 1124人閱讀

摘要:代碼實(shí)現(xiàn)啟動(dòng)啟動(dòng)流程見(jiàn)流程,主要包括守護(hù)進(jìn)程保存注冊(cè)信號(hào)處理器創(chuàng)建多進(jìn)程這部分。模擬調(diào)度實(shí)際用實(shí)現(xiàn)捕獲信號(hào)其中,會(huì)在每次調(diào)度過(guò)程中,捕獲信號(hào)并執(zhí)行注冊(cè)的信號(hào)處理器。

首發(fā)于 樊浩柏科學(xué)院

經(jīng)過(guò) 用 PHP 玩轉(zhuǎn)進(jìn)程之一 — 基礎(chǔ) 的回顧復(fù)習(xí),我們已經(jīng)掌握了進(jìn)程的基礎(chǔ)知識(shí),現(xiàn)在可以嘗試用 PHP 做一些簡(jiǎn)單的進(jìn)程控制和管理,來(lái)加深我們對(duì)進(jìn)程的理解。接下來(lái),我將用多進(jìn)程模型實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 PHPServer,基于它你可以做任何事。

PHPServer 完整的源代碼,可前往 fan-haobai/php-server 獲取。

總流程

該 PHPServer 的 Master 和 Worker 進(jìn)程主要控制流程,如下圖所示:

其中,主要涉及 3 個(gè)對(duì)象,分別為 入口腳本、Master 進(jìn)程、Worker 進(jìn)程。它們扮演的角色如下:

入口腳本:主要實(shí)現(xiàn) PHPServer 的啟動(dòng)、停止、重載功能,即觸發(fā) Master 進(jìn)程startstop、reload流程;

Master 進(jìn)程:負(fù)責(zé)創(chuàng)建并監(jiān)控 Worker 進(jìn)程。在啟動(dòng)階段,會(huì)注冊(cè)信號(hào)處理器,然后創(chuàng)建 Worker;在運(yùn)行階段,會(huì)持續(xù)監(jiān)控 Worker 進(jìn)程健康狀態(tài),并接受來(lái)自入口腳本的控制信號(hào)并作出響應(yīng);在停止階段,會(huì)停止掉所有 Worker 進(jìn)程;

Worker 進(jìn)程:負(fù)責(zé)執(zhí)行業(yè)務(wù)邏輯。在被 Master 進(jìn)程創(chuàng)建后,就處于持續(xù)運(yùn)行階段,會(huì)監(jiān)聽(tīng)到來(lái)自 Master 進(jìn)程的信號(hào),以實(shí)現(xiàn)自我的停止;

整個(gè)過(guò)程,又包括 4 個(gè)流程

流程 ① :以守護(hù)態(tài)啟動(dòng) PHPServer 時(shí)的主要流程。入口腳本會(huì)進(jìn)行 daemonize,也就是實(shí)現(xiàn)進(jìn)程的守護(hù)態(tài),此時(shí)會(huì)fork出一個(gè) Master 進(jìn)程;Master 進(jìn)程先經(jīng)過(guò) 保存 PID、注冊(cè)信號(hào)處理器 操作,然后 創(chuàng)建 Worker 會(huì)fork出多個(gè) Worker 進(jìn)程;

流程 ② :為 Master 進(jìn)程持續(xù)監(jiān)控的流程,過(guò)程中會(huì)捕獲入口腳本發(fā)送來(lái)的信號(hào)。主要監(jiān)控 Worker 進(jìn)程健康狀態(tài),當(dāng) Worker 進(jìn)程異常退出時(shí),會(huì)嘗試創(chuàng)建新的 Worker 進(jìn)程以維持 Worker 進(jìn)程數(shù)量;

流程 ③ :為 Worker 進(jìn)程持續(xù)運(yùn)行的流程,過(guò)程中會(huì)捕獲 Master 進(jìn)程發(fā)送來(lái)的信號(hào)。流程 ① 中 Worker 進(jìn)程被創(chuàng)建后,就會(huì)持續(xù)執(zhí)行業(yè)務(wù)邏輯,并阻塞于此;

流程 ④ :停止 PHPServer 的主要流程。入口腳本首先會(huì)向 Master 進(jìn)程發(fā)送 SIGINT 信號(hào),Master 進(jìn)程捕獲到該信號(hào)后,會(huì)向所有的 Worker 進(jìn)程轉(zhuǎn)發(fā) SIGINT 信號(hào)(通知所有的 Worker 進(jìn)程終止),等待所有 Worker 進(jìn)程終止退出;

在流程 ② 中,Worker 進(jìn)程被 Master 進(jìn)程fork出來(lái)后,就會(huì) 持續(xù)運(yùn)行 并阻塞于此,只有 Master 進(jìn)程才會(huì)繼續(xù)后續(xù)的流程。
代碼實(shí)現(xiàn) 啟動(dòng)

啟動(dòng)流程見(jiàn) 流程 ①,主要包括 守護(hù)進(jìn)程、保存 PID、注冊(cè)信號(hào)處理器、創(chuàng)建多進(jìn)程 Worker 這 4 部分。

守護(hù)進(jìn)程

首先,在入口腳本中fork一個(gè)子進(jìn)程,然后該進(jìn)程退出,并設(shè)置新的子進(jìn)程為會(huì)話組長(zhǎng),此時(shí)的這個(gè)子進(jìn)程就會(huì)脫離當(dāng)前終端的控制。如下圖所示:

這里使用了 2 次fork,所以最后fork的一個(gè)子進(jìn)程才是 Master 進(jìn)程,其實(shí)一次fork也是可以的。代碼如下:

protected static function daemonize()
{
    umask(0);
    $pid = pcntl_fork();
    if (-1 === $pid) {
        exit("process fork fail
");
    } elseif ($pid > 0) {
        exit(0);
    }

    // 將當(dāng)前進(jìn)程提升為會(huì)話leader
    if (-1 === posix_setsid()) {
        exit("process setsid fail
");
    }

    // 再次fork以避免SVR4這種系統(tǒng)終端再一次獲取到進(jìn)程控制
    $pid = pcntl_fork();
    if (-1 === $pid) {
        exit("process fork fail
");
    } elseif (0 !== $pid) {
        exit(0);
    }
}
通常在啟動(dòng)時(shí)增加-d參數(shù),表示進(jìn)程將運(yùn)行于守護(hù)態(tài)模式。

當(dāng)順利成為一個(gè)守護(hù)進(jìn)程后,Master 進(jìn)程已經(jīng)脫離了終端控制,所以有必要關(guān)閉標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。如下:

protected static function resetStdFd()
{
    global $STDERR, $STDOUT;
    //重定向標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出
    @fclose(STDOUT);
    fclose(STDERR);
    $STDOUT = fopen(static::$stdoutFile, "a");
    $STDERR = fopen(static::$stdoutFile, "a");
}
保存PID

為了實(shí)現(xiàn) PHPServer 的重載或停止,我們需要將 Master 進(jìn)程的 PID 保存于 PID 文件中,如php-server.pid文件。代碼如下:

protected static function saveMasterPid()
{
    // 保存pid以實(shí)現(xiàn)重載和停止
    static::$_masterPid = posix_getpid();
    if (false === file_put_contents(static::$pidFile, static::$_masterPid)) {
        exit("can not save pid to" . static::$pidFile . "
");
    }

    echo "PHPServer start