2、PHP Socket 网络编程 - 初探 PHP 多进程

作者: 温新

图书: 【PHP Socket 网络编程】

阅读: 224

时间: 2024-04-24 13:43:54

hi,我是温新,一名 PHPer

简说进程

进程是操作系统进行 CPU 资源分配和调度的最小单元。可以简单地理解为系统中运行的一个程序就是一个进程。

我们再用大白话来解释一下:我们写了一个 index.php,此时,它是一个安安静静躺在硬盘上面的 PHP 文件。没错,现在就是一个普通的文件,当我们运行这个文件时,它就变成了一个进程。

为啥要使用多进程

**多进程可以加快任务的完成速度。**比如,我们去餐馆吃饭,饭馆只有 1 个服务员,却有 3 个顾客。1 个服务员同一时刻只能服务 1 个顾客,那么剩下的 2 个顾客就只能干等着了。后面我们添加了 2 个服务员,这 2 个服务员就服务剩下的 2 个顾客。原本 1 个服务员服务完 3 个顾客可能要 3 分钟,现在,3 个服务员服务 3 个顾客只要 1 分钟。

那进程开的越多岂不是更好?当然不是,若设置不当反而会拖累系统。进程数的设置与 CPU 核数相关。

我们要知道,同一时刻,CPU 上只能执行一个进程。操作系统系统调度算法在多个进程之间快速切换 CPU,从而造成了一时间可以执行多个进程的假象。如我们一边用 PS 修图一边某个音乐客户端听音乐。

pcntl_fork

在 PHP 中可使用 pcntl_fork() 函数来创建多进程。使用该函数时,需要注意如下几点:

  • 子进程与父进程共享程序正文段
  • 子进程拥有父进程的数据空间、堆、栈的副本,存储空间相隔离
  • 父进程和子进程继续执行 fork 后的代码
  • 父、子进程谁先执行,取决于系统调度

虽说子进程会继承父进程的数据空间、堆、栈等数据,但并不是子进程一上来就要修改这些数据。如果子进程和父进程都是在读取数据,那么子进程使用的还是父进程中的数据。如果子进程要修改数据,此时,才会把父进程的数据复制到自己身上来,这用到了 COW(Copy On Write:写时复制)的技术。

pcntl_fork

我们还是先来看一下该函数的原型及返回,便于理解后续的操作。

pcntl_fork(): int

返回值:在父进程执行线程内返回产生的子进程的 PID,在子进程执行线程内返回 0。失败时,在 父进程上下文返回 -1。

白话解释一下返回值:在父进程处返回子进程 PID,在子进程处返回的 PID 为 0,若返回 -1 则报错。

创建子进程基本案例

<?php
// 2-1-pcntl.php 
    
$pid = pcntl_fork(); // $pid > 0

if ($pid > 0) {
    echo '我是父进程' . PHP_EOL;
} else if ($pid == 0) {
    echo '我是子进程' . PHP_EOL;
} else {
    echo '进程创建失败' . PHP_EOL;
}

我们执行看看效果

$ php 2-1-pcntl.php 
我是父进程
我是子进程

这段代码看起来有点傻里傻气的感觉,啥也没干,就是创建了一个进程,然后输出相关信息。但是我们要结合前面讲到的知识来理解这段代码。

pcntl_fork 创建出子进程生,父、子进程都从此处开始去执行后面的代码。$pid 是子进程的 PID 号且大于 0。因此,输出的信息我们也看到了。

进程之间空间隔离

我们通过一个修改数据来验证父、子进程之间数据的相互隔离。

<?php
// php 2-2-pcntl.php
    
$message = '来自于:';

$pid = pcntl_fork();

if ($pid > 0) {
    $message .= '父进程';
    echo $message . ',子进和 PID=' . $pid . ',当前进程 PID=' . posix_getpid() . PHP_EOL;
} else if ($pid == 0) {
    $message .= '子进程';
    echo $message . ',子进程 PID=' . posix_getpid() . ', 父进程 PID=' . posix_getppid() . PHP_EOL;
} else {
    echo '进程创建失败' . PHP_EOL;
}

输出结果

$ php 2-2-pcntl.php 
来自于:父进程,子进程 PID=56002,当前进程 PID=56001
来自于:子进程,子进程 PID=56002, 父进程 PID=56001

从这个案例中,我们至少要看到 父进程和子进程之间是相互隔离的

posix_getpid 函数用于返回当前进程 ID;

posix_getppid 函数用于返回父进程 ID。

本篇文章还没有结束,为了控制篇幅,本文结束。

下篇文章,我们在这个基础上学习一下多进程的创建及输出结果会是预期中的结果吗?

请登录后再评论