成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解 PHP 高性能框架 Workerman 守護(hù)進(jìn)程原理

開發(fā) 前端
守護(hù)進(jìn)程顧名思義就是能夠在后臺(tái)一直運(yùn)行的進(jìn)程,不會(huì)霸占用戶的會(huì)話終端,脫離了終端的控制。相信朋友們對(duì)這東西都不陌生了吧?如果連這個(gè)概念都還不能理解的話,建議回爐重造多看看 Linux 進(jìn)程管理相關(guān)的基礎(chǔ)知識(shí)。

大家好,我是碼農(nóng)先森。

守護(hù)進(jìn)程顧名思義就是能夠在后臺(tái)一直運(yùn)行的進(jìn)程,不會(huì)霸占用戶的會(huì)話終端,脫離了終端的控制。相信朋友們對(duì)這東西都不陌生了吧?如果連這個(gè)概念都還不能理解的話,建議回爐重造多看看 Linux 進(jìn)程管理相關(guān)的基礎(chǔ)知識(shí)。在我們?nèi)粘5木幊讨谐R娪蓄愃?nbsp;php think ...、php artisan ...、php yii ... 等命令啟動(dòng)需要一直執(zhí)行的任務(wù),都會(huì)通過 nohup 掛載到后臺(tái)保持長(zhǎng)期運(yùn)行的狀態(tài)。同樣在 Workerman 中也是使用類似 php index.php start 的命令來啟動(dòng)進(jìn)程,但不同的是它不需要利用 nohup 便可以掛載到后臺(tái)運(yùn)行。那有些朋友就會(huì)好奇它是怎么實(shí)現(xiàn)的呢?為了解決朋友們的疑惑,我們今天就重點(diǎn)深入分析一下 Workerman 守護(hù)進(jìn)程的實(shí)現(xiàn)原理。

我們先了解一些進(jìn)程相關(guān)的知識(shí):

  • 父進(jìn)程:父進(jìn)程是生成其他進(jìn)程的進(jìn)程。當(dāng)一個(gè)進(jìn)程創(chuàng)建了另一個(gè)進(jìn)程時(shí),創(chuàng)建者被稱為父進(jìn)程,而被創(chuàng)建的進(jìn)程則成為子進(jìn)程。父進(jìn)程可以通過進(jìn)程標(biāo)識(shí)符(PID)來識(shí)別它所創(chuàng)建的子進(jìn)程。
  • 子進(jìn)程:子進(jìn)程是由父進(jìn)程創(chuàng)建的新進(jìn)程。子進(jìn)程繼承了父進(jìn)程的一些屬性,例如環(huán)境變量、文件描述符等。子進(jìn)程獨(dú)立于父進(jìn)程運(yùn)行,它可以執(zhí)行自己的代碼,并且具有自己的資源和內(nèi)存空間。
  • 進(jìn)程組:進(jìn)程組是一組相關(guān)聯(lián)的進(jìn)程的集合。每個(gè)進(jìn)程組都有一個(gè)唯一的進(jìn)程組ID(PGID),用于標(biāo)識(shí)該進(jìn)程組。進(jìn)程組通常由一個(gè)父進(jìn)程創(chuàng)建,并且包含了與父進(jìn)程具有相同會(huì)話ID(SID)的所有子進(jìn)程。
  • 會(huì)話:會(huì)話是一組關(guān)聯(lián)進(jìn)程的集合,通常由用戶登錄到系統(tǒng)開始,直至用戶注銷或關(guān)閉終端會(huì)話結(jié)束,一個(gè)會(huì)話中的進(jìn)程共享相同的控制終端。每個(gè)會(huì)話都有一個(gè)唯一的會(huì)話ID(SID),用于標(biāo)識(shí)該會(huì)話。會(huì)話通常包含一個(gè)或多個(gè)進(jìn)程組,其中第一個(gè)進(jìn)程組成為會(huì)話的主進(jìn)程組。

這些概念俗稱八股文,向來都不怎么好理解,那我們來看個(gè)例子。執(zhí)行了命令 php index.php 便產(chǎn)生了進(jìn)程 61052「該進(jìn)程的父進(jìn)程是 Bash 進(jìn)程 8243,這里不用管它」,然后通過 Fork 創(chuàng)建了子進(jìn)程 61053 且其父進(jìn)程就是 61052,這兩個(gè)進(jìn)程擁有共同的進(jìn)程組 61052 和會(huì)話 8243。調(diào)用 posix_setsid 函數(shù),將會(huì)為子進(jìn)程 61053 開啟新的進(jìn)程組 61053 和新的會(huì)話 61053,這里的會(huì)話可以理解為一個(gè)新的命令窗口終端。最后子進(jìn)程 61053 通過 Fork 創(chuàng)建了子進(jìn)程 61054,進(jìn)程 61053 升級(jí)成了父進(jìn)程,這里再次 Fork 的原因是要避免被終端控制進(jìn)程所關(guān)聯(lián),這個(gè)進(jìn)程 61052 是在終端的模式下創(chuàng)建的,自此進(jìn)程 61054 就形成了守護(hù)進(jìn)程。

[manongsen@root phpwork]$ php index.php
[parent] 進(jìn)程ID: 61052, 父進(jìn)程ID: 8243, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243 
[parent1] 進(jìn)程ID: 61052, 父進(jìn)程ID: 8243, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243 退出了該進(jìn)程
[child1] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243 
[child1] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 
[parent2] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 退出了該進(jìn)程
[child2] 進(jìn)程ID: 61054, 父進(jìn)程ID: 61053, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 保留了該進(jìn)程

[manongsen@root phpwork]$ ps aux | grep index.php
root             66064   0.0  0.0 408105040   1472 s080  S+   10:00下午   0:00.00 grep index.php
root             61054   0.0  0.0 438073488    280   ??  S    10:00下午   0:00.00 php index.php

上面舉例的進(jìn)程信息,正是這段代碼運(yùn)行所產(chǎn)生的。如果看了這段代碼且細(xì)心的朋友,會(huì)發(fā)現(xiàn)為什么 posix_setsid 這個(gè)函數(shù)不放在第一次 Fork 前調(diào)用,而在第二次 Fork 前調(diào)用呢,這樣的話就不用 Fork 兩次了?原因是組長(zhǎng)進(jìn)程是不能創(chuàng)建會(huì)話的,進(jìn)程組ID 61052 和進(jìn)程ID 61052 相同「即當(dāng)前進(jìn)程則為組長(zhǎng)進(jìn)程」,所以需要子進(jìn)程來創(chuàng)建新的會(huì)話,這一點(diǎn)需要特別注意一下。

<?php

function echoMsg($prefix, $suffix="") {
    // 進(jìn)程ID
    $pid = getmypid(); 
    // 進(jìn)程組ID
    $pgid = posix_getpgid($pid);
    // 會(huì)話ID
    $sid = posix_getsid($pid); 
    // 父進(jìn)程ID
    $ppid = posix_getppid();

    echo "[{$prefix}] 進(jìn)程ID: {$pid}, 父進(jìn)程ID: {$ppid}, 進(jìn)程組ID: {$pgid}, 會(huì)話ID: {$sid} {$suffix}" . PHP_EOL;
}

// [parent] 進(jìn)程ID: 61052, 父進(jìn)程ID: 8243, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243
echoMsg("parent");

// 第一次 Fork 進(jìn)程  
$pid = pcntl_fork();
if ( $pid < 0 ) {
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent1] 進(jìn)程ID: 61052, 父進(jìn)程ID: 8243, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243 退出了該進(jìn)程
    echoMsg("parent1", "退出了該進(jìn)程");
    exit;
}

// 創(chuàng)建的 子進(jìn)程ID 為 61053 但 進(jìn)程組、會(huì)話 還是和父進(jìn)程是同一個(gè)
// [child1] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61052, 會(huì)話ID: 8243 
echoMsg("child1");

// 調(diào)用 posix_setsid 函數(shù),會(huì)創(chuàng)建一個(gè)新的會(huì)話和進(jìn)程組,并設(shè)置 進(jìn)程組ID 和 會(huì)話ID 為該 進(jìn)程ID
if (-1 === \posix_setsid()) {
    throw new Exception("Setsid fail");
}

// 現(xiàn)在會(huì)發(fā)現(xiàn) 進(jìn)程組ID 和 會(huì)話ID 都變成了 61053 在這里相當(dāng)于啟動(dòng)了一個(gè)類似 Linux 終端下的會(huì)話窗口
// [child1] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 
echoMsg("child1");

// 第二次 Fork 進(jìn)程
// 這里需要二次 Fork 進(jìn)程的原因是避免被終端控制進(jìn)程所關(guān)聯(lián),這個(gè)進(jìn)程 61052 是在終端的模式下創(chuàng)建的
// 需要脫離這個(gè)進(jìn)程 61052 以確保守護(hù)進(jìn)程的穩(wěn)定
$pid = pcntl_fork();
if ( $pid  < 0 ){
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent2] 進(jìn)程ID: 61053, 父進(jìn)程ID: 61052, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 退出了該進(jìn)程
    echoMsg("parent2", "退出了該進(jìn)程");
    exit;
}

// 到這里該進(jìn)程已經(jīng)脫離了終端進(jìn)程的控制,形成了守護(hù)進(jìn)程
// [child2] 進(jìn)程ID: 61054, 父進(jìn)程ID: 61053, 進(jìn)程組ID: 61053, 會(huì)話ID: 61053 保留了該進(jìn)程
echoMsg("child2", "保留了該進(jìn)程");

sleep(100);

有時(shí)間的朋友最好自行執(zhí)行代碼并分析一遍,會(huì)有不一樣的收獲。這里假裝你已經(jīng)實(shí)踐過了,這下我們來看 Workerman 的 Worker.php 文件中 554 行的 runAll 方法中的 static::daemonize() 這個(gè)函數(shù),實(shí)現(xiàn)的流程邏輯和上面的例子幾乎一樣。不過這里還使用了 umask 這個(gè)函數(shù),其主要的作用是為該進(jìn)程所創(chuàng)建的文件或目錄賦予相應(yīng)的權(quán)限,保證有權(quán)限操作文件或目錄。

// workerman/Worker.php:554
/**
 * Run all worker instances.
 * 運(yùn)行進(jìn)程
 * @return void
 */
public static function runAll()
{
    static::checkSapiEnv();
    static::init();
    static::parseCommand();
    static::lock();
    // 創(chuàng)建進(jìn)程并形成守護(hù)進(jìn)程
    static::daemonize();
    static::initWorkers();
    static::installSignal();
    static::saveMasterPid();
    static::lock(\LOCK_UN);
    static::displayUI();
    static::forkWorkers();
    static::resetStd();
    static::monitorWorkers();
}

// workerman/Worker.php:1262
/**
 * Run as daemon mode.
 * 使用守護(hù)進(jìn)程模式運(yùn)行
 * @throws Exception
 */
protected static function daemonize()
{
 // 判斷是否已經(jīng)是守護(hù)狀態(tài)、以及當(dāng)前系統(tǒng)是否是 Linux 環(huán)境
    if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
        return;
    }
    
    // 設(shè)置 umask 為 0 則當(dāng)前進(jìn)程創(chuàng)建的文件權(quán)限都為 777 擁有最高權(quán)限
    \umask(0);
    
    // 第一次創(chuàng)建進(jìn)程
    $pid = \pcntl_fork();
    if (-1 === $pid) {
     // 創(chuàng)建進(jìn)程失敗
        throw new Exception('Fork fail');
    } elseif ($pid > 0) {
     // 主進(jìn)程退出
        exit(0);
    }

 // 子進(jìn)程繼續(xù)執(zhí)行...
    // 調(diào)用 posix_setsid 函數(shù),可以讓進(jìn)程脫離父進(jìn)程,轉(zhuǎn)變?yōu)槭刈o(hù)進(jìn)程
    if (-1 === \posix_setsid()) {
        throw new Exception("Setsid fail");
    }

 // 第二次創(chuàng)建進(jìn)程,在基于 System V 的系統(tǒng)中,通過再次 Fork 父進(jìn)程退出
 // 保證形成的守護(hù)進(jìn)程,不會(huì)成為會(huì)話首進(jìn)程,不會(huì)擁有控制終端
    $pid = \pcntl_fork();
    if (-1 === $pid) {
     // 創(chuàng)建進(jìn)程失敗
        throw new Exception("Fork fail");
    } elseif (0 !== $pid) {
     // 主進(jìn)程退出
        exit(0);
    }

    // 子進(jìn)程繼續(xù)執(zhí)行...
}

守護(hù)進(jìn)程也是 Workerman 中重要的一部分,它保障了 Workerman 進(jìn)程的穩(wěn)定性。不像我們通過 nohup 啟動(dòng)的命令,掛起到后臺(tái)之后,有時(shí)還神不知鬼不覺的就掛了,朋友們或許都有這樣的經(jīng)歷吧。當(dāng)然在市面上也有一些開源的守護(hù)進(jìn)程管理軟件,比如 supervisor 等,其次還有人利用會(huì)話終端 screen、tmux 等工具來實(shí)現(xiàn)。其實(shí)守護(hù)進(jìn)程的實(shí)現(xiàn)方式有多種多樣,我們這里只是為了分析 Workerman 中守護(hù)進(jìn)程的實(shí)現(xiàn)原理,而引出了在 PHP 中實(shí)現(xiàn)守護(hù)進(jìn)程模式的例子,希望本次的內(nèi)容能對(duì)你有所幫助。

責(zé)任編輯:武曉燕 來源: 碼農(nóng)先森
相關(guān)推薦

2025-01-13 13:00:00

Go網(wǎng)絡(luò)框架nbio

2025-01-26 00:00:15

PHP協(xié)程控制權(quán)

2009-11-16 17:20:04

PHP多維數(shù)組排序

2024-10-21 09:06:15

2022-11-09 08:12:07

2019-04-08 16:50:33

前端性能監(jiān)控

2024-03-12 00:00:00

Sora技術(shù)數(shù)據(jù)

2022-11-04 09:43:05

Java線程

2021-03-10 10:55:51

SpringJava代碼

2022-09-05 08:39:04

kubernetesk8s

2024-11-01 08:57:07

2024-04-15 00:00:00

技術(shù)Attention架構(gòu)

2020-08-10 18:03:54

Cache存儲(chǔ)器CPU

2020-12-04 11:40:53

Linux

2020-03-26 16:40:07

MySQL索引數(shù)據(jù)庫(kù)

2023-09-19 22:47:39

Java內(nèi)存

2022-01-14 12:28:18

架構(gòu)OpenFeign遠(yuǎn)程

2022-09-26 08:01:31

線程LIFO操作方式

2020-11-04 15:35:13

Golang內(nèi)存程序員

2020-03-17 08:36:22

數(shù)據(jù)庫(kù)存儲(chǔ)Mysql
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 二区中文字幕 | 国产成人精品一区二区三区四区 | 81精品国产乱码久久久久久 | 国产高清亚洲 | 三级特黄特色视频 | 日韩在线视频免费观看 | 国产综合久久久久久鬼色 | 国产精品久久久久久久久久免费看 | 日本在线一二 | 国产精品欧美一区二区 | 精品久久久久久久久久久久 | a中文在线视频 | 欧美jizzhd精品欧美巨大免费 | 中文字幕一区二区三区四区五区 | 日韩一级免费看 | 成人夜晚看av | 一区二区高清不卡 | 午夜视频在线免费观看 | 天天插天天舔 | av在线免费网 | 欧美成人一区二区三区 | 国产精品99久久久久久人 | 久久久网| 国产xxxx搡xxxxx搡麻豆 | 91精品国产色综合久久不卡98口 | 成人av一区二区三区 | 精品日韩一区 | 亚洲精品视频在线播放 | 精品国产一区二区三区久久 | 美女久久 | 日韩在线免费 | 亚洲欧美久久 | 久久久久久久国产精品视频 | 91av在线电影| 91国内精品久久 | 久久久久国产精品免费免费搜索 | 黄色一级视频 | 日本a∨精品中文字幕在线 亚洲91视频 | 免费视频久久久久 | 欧美日韩精品中文字幕 | 91久久久久 |