PHP如何使用共享内存_高并发共享内存操作指南【教程】

3次阅读

php共享内存必须使用shmop系列函数,因原生不支持posix共享内存;需注意键唯一性、大小固定、手动同步(信号量或文件锁)、序列化处理及替代方案如apcu/redis

PHP如何使用共享内存_高并发共享内存操作指南【教程】

PHP共享内存函数必须用shmop系列

PHP原生不支持POSIX共享内存(如shmget/shmat),只能用shmop_openshmop_readshmop_write这一套。别试图用pcntl_fork配合普通变量或apcu_store来“模拟”共享内存——前者变量完全隔离,后者是缓存而非进程间共享内存。

常见错误现象:shmop_open返回false但没报错;shmop_write写入后其他进程读不到;反复调用shmop_open导致段泄漏。

  • shmop_open$key参数必须是系统级唯一整数(如0x1234),不能用字符串或随机数,否则跨进程无法定位同一段
  • 大小必须提前预估并固定,shmop_open不支持动态扩容,写超会静默截断
  • 写入前必须用shmop_size确认当前段长度,避免越界;读取时也要检查返回值是否为false或空字符串
  • 进程退出前务必调用shmop_delete + shmop_close,否则段残留,ipcs -m能看到“nattch=0 but not destroyed”

并发下shmop_write不是原子操作

多个PHP-FPM子进程同时shmop_write同一块内存,会出现数据覆盖或错位,因为该函数底层只是memcpy,没有锁机制。

解决办法只有加外部同步:要么用sem_acquiresem_get信号量,要么用文件锁(flock)兜底。但注意:sem_*函数在PHP 8.0+默认禁用,需编译时启用--enable-sysvsem,且信号量键值也得和共享内存键值保持逻辑关联。

立即学习PHP免费学习笔记(深入)”;

  • 推荐组合:sem_get($shm_key + 1) + sem_acquire,避免信号量键冲突
  • 不要在shmop_write前后sleep或做耗时操作,否则锁持有时间过长,成为瓶颈
  • 如果只是计数器场景,优先考虑apcu_inc线程安全)或Redis INCR,比手动管理shmop+sem轻量得多

shmop不支持结构化数据,序列化要自己处理

shmop_write只接受String,你不能直接写ArrayObject。必须用serialize/unserializejson_encode/json_decode,但要注意:

  • serialize结果含PHP版本和类名信息,不同PHP版本间可能反序列化失败;json_encode更安全,但不支持资源、闭包循环引用
  • 写入前必须检查序列化后长度是否≤共享内存段大小,否则shmop_write返回0且无提示
  • 读取后unserialize前应先校验字符串非空、非false,并用mb_strlen确认长度,防止被截断的脏数据引发致命错误

替代方案比硬啃shmop更实用

真正高并发生产环境几乎不用shmop:它难调试、无持久化、无自动清理、不兼容容器化部署(/dev/shm挂载点受限)、PHP扩展依赖强。

更现实的选择:

  • 短生命周期计数/开关:用apcu_store('flag', true, 30),APCu在PHP-FPM多进程间共享,且带TTL
  • 需要跨机器:上Redis,哪怕单节点,INCR/GETSET原语比自己实现锁可靠得多
  • 纯本地高频通信(如Worker进程协同):改用unix Domain Socket或消息队列(e.g. Beanstalkd),语义清晰、可监控、易扩缩

shmop只适合极少数场景:比如一个常驻PHP守护进程(非FPM)与若干子进程交换固定格式二进制状态,且对毫秒级延迟敏感——这种需求本身已非常边缘。

text=ZqhQzanResources