PHP进程间通信的几种方法详解

陆景行
陆景行
发布时间:2026-10-13 | 4人浏览过
原创

在PHP多进程开发中,进程间通信(IPC)是协调工作单元的核心。本文深入解析管道(Pipe)、消息队列(Message Queue)及共享内存+信号量这三种基于POSIX扩展的实现方案,提供可直接运行的PHP代码示例,帮助开发者掌握如何在父子进程间高效传输数据并确保同步。

管道通信 PIPE

管道(Pipe)是一种最简单的 IPC 机制,可视为一种特殊的临时文件。其核心特性是具有固定的读写端:一端只能写入,另一端只能读取。在 PHP 中,虽然可以使用普通文件模拟,但使用 POSIX 管道能更好地利用内核缓冲区的单向流特性。

第1步:创建命名管道

使用 posix_mkfifo 函数创建命名管道,这允许无关进程或通过文件系统路径进行通信,而不仅仅是具有亲缘关系的进程(如 fork 后的父子进程)。操作权限通常设为 0666。

<?php
$pipePath = "pipe";
// 确保管道存在
if ( !file_exists($pipePath) ) {
    if ( !posix_mkfifo($pipePath, 0666) ) {
        exit('make pipe false!' . PHP_EOL);
    }
}

第2步:生成进程并分配读写角色

调用 pcntl_fork() 创建子进程。该函数返回值具有特殊逻辑:

  • 父进程:返回子进程的 PID(大于 0)。
  • 子进程:返回 0。
  • 失败:返回 -1。
$pid = pcntl_fork();
if ($pid == 0) {
    // --- 子进程:写入数据 ---
    $file = fopen($pipePath, 'w');
    fwrite($file, 'hello world');
    fclose($file);
    sleep(1);
    exit;
} else {
    // --- 父进程:读取数据 ---
    // 注意:非阻塞模式需配合 stream_set_blocking 设置
    $file = fopen($pipePath, 'r');
    echo fread($file, 20) . PHP_EOL;
    fclose($file);
    
    // 回收子进程资源
    pcntl_wait($status);
}

SYSV 消息队列

消息队列(Message Queue)将消息存放在内核中的链表里。相比管道,它具有以下优势: 1. **独立性**:进程无需具有亲缘关系即可通信,只要知道唯一的 Key。 2. **结构化**:支持自定义消息类型(message type),消费端可根据类型选择性读取,而非严格遵循 FIFO 顺序。

第1步:初始化队列与生成 Key

使用 ftok() 根据文件路径和资源标识符生成 System V IPC Key。通过 msg_get_queue() 获取队列句柄。

$id = ftok(__FILE__, 'm');
$msgQueue = msg_get_queue($id);
const MSG_TYPE = 1;

第2步:实现生产者与消费者模型

定义通用的 fork 辅助函数来隔离主逻辑。生产者使用 msg_send() 发送带有指定类型的消息;消费者使用 msg_receive() 接收消息。

function producer() {
    global $msgQueue;
    $pid = posix_getpid();
    for ($i = 0; $i <= 5; $i++) {
        $str = "({$pid}) progress create! {$i}";
        // 参数:资源,类型,序列号,缓冲区大小,接收变量
        msg_send($msgQueue, MSG_TYPE, $str);
        sleep(rand(1, 3));
    }
}

function consumer() {
    global $msgQueue;
    $pid = posix_getpid();
    for ($i = 1; $i <= 6; $i++) {
        $rel = msg_receive($msgQueue, 0, $msgType, 1024, $message);
        if ($rel) {
            echo "{$message} | consumer({$pid}) receive\n";
        }
        sleep(rand(1, 3));
    }
}

// 执行逻辑:创建3个生产者,2个消费者
// ...省略fork循环部分...
while (!empty($childList)) {
    $childPid = pcntl_wait($status);
    if ($childPid > 0) {
        unset($childList[$childPid]);
    }
}

共享内存与信号量

共享内存(Shared Memory)允许多个进程访问同一段物理内存区域,这是最快的 IPC 方式。但由于多个进程同时读写可能导致数据竞争(Race Condition),必须配合信号量(Semaphore)来实现互斥锁机制。

第1步:配置共享内存与信号量

分别生成两个不同的 Key:m 用于共享内存,s 用于信号量。使用 shm_attach() 和 sem_get() 建立连接。

// 创建共享内存 ID
$shm_key = ftok(__FILE__, 'm');
$shareMemory = shm_attach($shm_key);

// 创建信号量 ID
$sem_key = ftok(__FILE__, 's');
$sig = sem_get($sem_key); // 默认计数1,即排他锁
const SHARE_KEY = 1;

第2步:加锁保护临界区

在修改共享内存之前,必须先调用 sem_acquire($sig) 获取锁。该调用会阻塞进程直到获得锁。操作完成后,调用 sem_release($sig) 释放锁。

function producer() {
    global $shareMemory;
    global $sig;
    $pid = posix_getpid();
    
    for ($i = 1; $i <= 5; $i++) {
        // 1. 获取锁(阻塞直至可用)
        sem_acquire($sig);
        
        // 2. 执行受保护的代码块
        if (shm_has_var($shareMemory, SHARE_KEY)) {
            $count = shm_get_var($shareMemory, SHARE_KEY);
            $count++;
            shm_put_var($shareMemory, SHARE_KEY, $count);
            echo "({$pid}) count: {$count}\n";
        } else {
            shm_put_var($shareMemory, SHARE_KEY, 1);
            echo "({$pid}) count: 1 (init)\n";
        }
        
        // 3. 释放锁
        sem_release($sig);
        
        usleep(100000); // 短暂休眠以观察结果
    }
}

完成判断

当所有进程结束并等待回收后,记得清理系统资源:

shm_remove($shareMemory);
sem_remove($sig);

简要总结

本文梳理了 PHP 中三种经典的进程间通信手段。管道适合简单的父子进程流式数据传输;消息队列适合解耦的生产者-消费者模式且需要灵活路由消息的场景;共享内存+信号量则是追求极致性能并需处理复杂状态同步时的最佳选择。在实际选型时,应根据对速度、耦合度及实现复杂度的要求权衡。

以上就是PHP进程间通信的几种方法详解的详细内容,更多关于PHP进程间通信IPC的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表