Workerman 中 sysvshm 无法实现多进程共享内存,因其 shm_attach() 返回的指针是进程私有的,fork 后子进程无法继承;正确做法是各进程独立用 ftok 生成 key 并调用 shm_attach/shm_get_var 等,或改用更可靠的 shmop 扩展。

Workerman 无法直接用 sysvshm 实现“极致”的多进程共享内存——不是因为做不到,而是它根本不能用。 sysvshm 扩展在 PHP 多进程模型下存在严重设计缺陷:shm_attach() 返回的资源句柄是进程私有的,子进程 fork 后无法继承该句柄,且 shm_get_var() / shm_put_var() 内部依赖的底层 shm ID 不跨进程复用,导致父子进程操作的是逻辑隔离的内存段。你看到“写入成功、读取为空”,大概率就是这个原因。
为什么 fork 后 sysvshm 句柄失效?
PHP 的 sysvshm 扩展封装的是 System V IPC 的 shmat() 系统调用,返回的是当前进程地址空间内的指针(即 void *),不是全局句柄。Workerman 启动后主进程 fork() 出多个 worker 子进程,每个子进程都有自己独立的虚拟地址空间,父进程调用 shm_attach() 得到的指针对子进程完全无效。即使你手动在子进程中重新 shm_attach() 同一个 key,也得确保 key 全局一致、权限可访问,且必须各自调用 shm_remove() 避免残留。
常见错误现象:
- 主进程写入
shm_put_var($shm_id, 1, 'ok'),子进程调用shm_get_var($shm_id, 1)返回false - 子进程单独 attach 成功,但读不到主进程写的数据(因为压根没连到同一块物理段)
- 忘记在进程退出前调用
shm_detach()和shm_remove(),导致ipcs -m中残留大量0x00000000段,最终报Invalid argument
Workerman 中真正可用的 sysvshm 写法
必须让每个 worker 进程独立完成“key → attach → 读写 → detach → remove”全链路,且 key 必须通过 ftok() 稳定生成(不能硬编码整数,否则易冲突)。关键点是:所有进程操作的是同一个 System V 共享内存段,而非共享句柄。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 统一用
ftok(__FILE__, 'w')生成 key,避免不同文件或字符导致 key 不一致 - worker 启动时立即调用
$shm_id = shm_attach($key, $size, 0644);失败则exit,不要重试——说明系统限制已达上限 - 读写必须用
shm_put_var()/shm_get_var(),不要混用shmop系列函数(二进制格式不兼容) - 进程退出前务必执行
shm_detach($shm_id)和shm_remove($shm_id),否则下次启动会因段已存在而 attach 失败 - 若需高频读写,避免在每次请求中反复 attach/detach——应在 Worker::onWorkerStart 中 attach,onWorkerStop 中 detach/remove
shmop 才是 Workerman 更现实的选择
shmop 虽然功能简单,但它的 shmop_open() 返回的是可跨进程复用的资源 ID(本质是文件描述符),且 API 更贴近 System V 共享内存语义。Workerman 官方文档和主流实践都倾向用它。
对比差异:
-
shmop支持"c"(创建)、"a"(附加)、"w"(写入)模式,worker 可安全地先open("a")尝试附加,失败再open("c")创建 -
shmop_write()和shmop_read()直接操作字节偏移,无序列化开销,适合结构化数据(如 pack/unpack) -
shmop_size()可动态获取段大小,shmop_delete()显式清理,行为更可控 - Windows 下不可用,但 Workerman 本身也不支持 Windows 生产部署,不影响实际使用
示例关键片段:
$key = ftok(__FILE__, 'x');
$shm_id = shmop_open($key, "a", 0, 0);
if (!$shm_id) {
$shm_id = shmop_open($key, "c", 0644, 1024 * 1024); // 1MB
}
// 写入:shmop_write($shm_id, json_encode($data), 0);
// 读取:json_decode(shmop_read($shm_id, 0, 1024 * 1024), true);
真正容易被忽略的点:sysvshm 的 shm_get_var() 底层会对数据做 PHP 序列化/反序列化,不仅慢,还隐含 unserialize() 安全风险;而 shmop 是裸字节操作,格式、边界、长度全由你控制——这对 Workerman 高频通信场景反而是优势。别迷信“System V”名字,能跑通、不出错、不泄漏,才是极致。



















