PHP进程间通信的几种方法详解
在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的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













