当前位置:首页 > 后端开发 > 正文内容

深化了解 PHP 高性能结构 Workerman 看护进程原理

邻居的猫1个月前 (12-09)后端开发622

我们好,我是码农先森。

看护进程望文生义便是能够在后台一向运转的进程,不会强占用户的会话终端,脱离了终端的操控。信任朋友们对这东西都不生疏了吧?假如连这个概念都还不能了解的话,主张回炉重造多看看 Linux 进程办理相关的基础常识。在咱们日常的编程中常见有相似 php think ...php artisan ...php yii ... 等指令发动需求一向履行的使命,都会经过 nohup 挂载到后台坚持长时间运转的状况。同样在 Workerman 中也是运用相似 php index.php start 的指令来发动进程,但不同的是它不需求运用 nohup 便能够挂载到后台运转。那有些朋友就会猎奇它是怎样完成的呢?为了处理朋友们的疑问,咱们今日就要点深入剖析一下 Workerman 看护进程的完成原理。

咱们先了解一些进程相关的常识:

  • 父进程:父进程是生成其他进程的进程。当一个进程创立了另一个进程时,创立者被称为父进程,而被创立的进程则成为子进程。父进程能够经过进程标识符(PID)来辨认它所创立的子进程。
  • 子进程:子进程是由父进程创立的新进程。子进程承继了父进程的一些特点,例如环境变量、文件描述符等。子进程独立于父进程运转,它能够履行自己的代码,而且具有自己的资源和内存空间。
  • 进程组:进程组是一组相相关的进程的调集。每个进程组都有一个仅有的进程组ID(PGID),用于标识该进程组。进程组一般由一个父进程创立,而且包括了与父进程具有相同会话ID(SID)的一切子进程。
  • 会话:会话是一组相关进程的调集,一般由用户登录到体系开端,直至用户刊出或封闭终端会话完毕,一个会话中的进程同享相同的操控终端。每个会话都有一个仅有的会话ID(SID),用于标识该会话。会话一般包括一个或多个进程组,其间第一个进程组成为会话的主进程组。

这些概念俗称八股文,历来都不怎样好了解,那咱们来看个比方。履行了指令 php index.php 便发生了进程 61052「该进程的父进程是 Bash 进程 8243,这儿不必管它」,然后经过 Fork 创立了子进程 61053 且其父进程便是 61052,这两个进程具有一起的进程组 61052 和会话 8243。调用 posix_setsid 函数,将会为子进程 61053 敞开新的进程组 61053 和新的会话 61053,这儿的会话能够了解为一个新的指令窗口终端。最终子进程 61053 经过 Fork 创立了子进程 61054,进程 61053 晋级成了父进程,这儿再次 Fork 的原因是要防止被终端操控进程所相关,这个进程 61052 是在终端的形式下创立的,自此进程 61054 就构成了看护进程。

[manongsen@root phpwork]$ php index.php
[parent] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 
[parent1] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 退出了该进程
[child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61052, 会话ID: 8243 
[child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 
[parent2] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 退出了该进程
[child2] 进程ID: 61054, 父进程ID: 61053, 进程组ID: 61053, 会话ID: 61053 保留了该进程

[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

上面举例的进程信息,正是这段代码运转所发生的。假如看了这段代码且仔细的朋友,会发现为什么 posix_setsid 这个函数不放在第一次 Fork 前调用,而在第2次 Fork 前调用呢,这样的话就不必 Fork 两次了?原因是组长进程是不能创立会话的,进程组ID 61052 和进程ID 61052 相同「即当时进程则为组长进程」,所以需求子进程来创立新的会话,这一点需求特别注意一下。

<?php

function echoMsg($prefix, $suffix="") {
    // 进程ID
    $pid = getmypid(); 
    // 进程组ID
    $pgid = posix_getpgid($pid);
    // 会话ID
    $sid = posix_getsid($pid); 
    // 父进程ID
    $ppid = posix_getppid();

    echo "[{$prefix}] 进程ID: {$pid}, 父进程ID: {$ppid}, 进程组ID: {$pgid}, 会话ID: {$sid} {$suffix}" . PHP_EOL;
}

// [parent] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243
echoMsg("parent");

// 第一次 Fork 进程  
$pid = pcntl_fork();
if ( $pid < 0 ) {
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent1] 进程ID: 61052, 父进程ID: 8243, 进程组ID: 61052, 会话ID: 8243 退出了该进程
    echoMsg("parent1", "退出了该进程");
    exit;
}

// 创立的 子进程ID 为 61053 但 进程组、会话 仍是和父进程是同一个
// [child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61052, 会话ID: 8243 
echoMsg("child1");

// 调用 posix_setsid 函数,会创立一个新的会话和进程组,并设置 进程组ID 和 会话ID 为该 进程ID
if (-1 === \posix_setsid()) {
    throw new Exception("Setsid fail");
}

// 现在会发现 进程组ID 和 会话ID 都变成了 61053 在这儿相当于发动了一个相似 Linux 终端下的会话窗口
// [child1] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 
echoMsg("child1");

// 第2次 Fork 进程
// 这儿需求二次 Fork 进程的原因是防止被终端操控进程所相关,这个进程 61052 是在终端的形式下创立的
// 需求脱离这个进程 61052 以确保看护进程的安稳
$pid = pcntl_fork();
if ( $pid  < 0 ){
    exit('fork error');
} else if( $pid > 0 ) {
    // [parent2] 进程ID: 61053, 父进程ID: 61052, 进程组ID: 61053, 会话ID: 61053 退出了该进程
    echoMsg("parent2", "退出了该进程");
    exit;
}

// 到这儿该进程现已脱离了终端进程的操控,构成了看护进程
// [child2] 进程ID: 61054, 父进程ID: 61053, 进程组ID: 61053, 会话ID: 61053 保留了该进程
echoMsg("child2", "保留了该进程");

sleep(100);

有时间的朋友最好自行履行代码并剖析一遍,会有不相同的收成。这儿伪装你现已实践过了,这下咱们来看 Workerman 的 Worker.php 文件中 554 行的 runAll 办法中的 static::daemonize() 这个函数,完成的流程逻辑和上面的比方简直相同。不过这儿还运用了 umask 这个函数,其主要的作用是为该进程所创立的文件或目录赋予相应的权限,确保有权限操作文件或目录。

// workerman/Worker.php:554
/**
 * Run all worker instances.
 * 运转进程
 * @return void
 */
public static function runAll()
{
    static::checkSapiEnv();
    static::init();
    static::parseCommand();
    static::lock();
    // 创立进程并构成看护进程
    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.
 * 运用看护进程形式运转
 * @throws Exception
 */
protected static function daemonize()
{
	// 判别是否现已是看护状况、以及当时体系是否是 Linux 环境
    if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
        return;
    }
    
    // 设置 umask 为 0 则当时进程创立的文件权限都为 777 具有最高权限
    \umask(0);
    
    // 第一次创立进程
    $pid = \pcntl_fork();
    if (-1 === $pid) {
    	// 创立进程失利
        throw new Exception('Fork fail');
    } elseif ($pid > 0) {
    	// 主进程退出
        exit(0);
    }

	// 子进程持续履行...
    // 调用 posix_setsid 函数,能够让进程脱离父进程,转变为看护进程
    if (-1 === \posix_setsid()) {
        throw new Exception("Setsid fail");
    }

	// 第2次创立进程,在根据 System V 的体系中,经过再次 Fork 父进程退出
	// 确保构成的看护进程,不会成为会话首进程,不会具有操控终端
    $pid = \pcntl_fork();
    if (-1 === $pid) {
    	// 创立进程失利
        throw new Exception("Fork fail");
    } elseif (0 !== $pid) {
    	// 主进程退出
        exit(0);
    }

    // 子进程持续履行...
}

看护进程也是 Workerman 中重要的一部分,它保证了 Workerman 进程的安稳性。不像咱们经过 nohup 发动的指令,挂起到后台之后,有时还神不知鬼不觉的就挂了,朋友们或许都有这样的阅历吧。当然在市面上也有一些开源的看护进程办理软件,比方 supervisor 等,其次还有人运用会话终端 screen、tmux 等东西来完成。其实看护进程的完成方法有多种多样,咱们这儿仅仅为了剖析 Workerman 中看护进程的完成原理,而引出了在 PHP 中完成看护进程形式的比方,期望本次的内容能对你有所协助。

感谢我们阅览,个人观念仅供参考,欢迎在谈论区宣布不同观念。


欢迎重视、共享、点赞、保藏、在看,我是微信大众号「码农先森」作者。

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=195

分享给朋友:

“深化了解 PHP 高性能结构 Workerman 看护进程原理” 的相关文章

【日记】自己心里戏很多(笑(968 字)

【日记】自己心里戏很多(笑(968 字)

正文   本来想手写来着,成果找了快一个小时的图。没找到。抛弃了。时间也不大够用了,就不手写了。   找图首要是由于一件事——今日遽然告诉要拍证件照。   我特别疑惑,之前不是拍过了吗,并且也没怎样用到,这东西。如同必需要从头拍,不知道为什么。并且正午才告诉。   还必需要打领带……   谁没事儿系...

swift翻译,Swift编程语言简介

swift翻译,Swift编程语言简介

Swift 是一种编程语言,主要用于 iOS、macOS、watchOS 和 tvOS 的开发。它由苹果公司于 2014 年推出,旨在替代 ObjectiveC,成为苹果生态系统的主要编程语言。Swift 具有简洁、安全、快速和易学的特点,深受开发者喜爱。如果您是指将 Swift 代码翻译成其他语言...

python定义一个变量,Python变量定义详解

python定义一个变量,Python变量定义详解

我已经定义了一个名为 `my_variable` 的变量,其值为 42。现在这个变量的值是 42。Python变量定义详解在Python编程语言中,变量是存储数据的基本单元。理解如何定义和使用变量对于编写有效的Python代码至关重要。本文将详细介绍Python中变量的定义方法、规则以及一些实用的技...

python值得学吗,Python值得学吗?——全面解析Python的学习价值与前景

python值得学吗,Python值得学吗?——全面解析Python的学习价值与前景

学习Python是一个很好的选择,原因如下:1. 广泛的应用领域:Python被广泛应用于数据科学、机器学习、人工智能、Web开发、自动化脚本等多个领域。这使得Python成为多面手,可以应对各种不同的编程需求。2. 简洁的语法:Python以其简洁明了的语法著称,使得它非常适合初学者。其语法接近于...

php安全,构建安全可靠的Web应用

php安全,构建安全可靠的Web应用

1. 输入验证:确保所有用户输入都经过验证和清理,以防止SQL注入、跨站脚本(XSS)等攻击。2. 数据库安全:使用预处理语句和参数化查询来防止SQL注入攻击。确保数据库用户具有最小权限,并定期更新数据库软件以修复已知漏洞。3. 文件上传:限制文件类型和大小,验证文件内容,并确保上传的文件不会覆盖现...

go省电,GO省电——智能电池管理,助你轻松延长手机续航

go省电,GO省电——智能电池管理,助你轻松延长手机续航

为了在Go语言中实现省电效果,我们可以采取以下策略:1. 优化循环和条件判断:减少不必要的循环迭代和条件判断,避免重复计算。2. 使用更高效的数据结构:选择合适的数据结构来存储和处理数据,以减少内存使用和CPU消耗。3. 避免阻塞操作:使用非阻塞操作和异步编程,避免程序长时间占用CPU。4. 减少内...