php方案 tmpfs 与共享内存速度对比: PHP 进程将高频读写的临时数据放在 /dev/shm(tmpfs)与使用 shmop 共享内存段

发布时间:2026/5/20 12:20:18

php方案 tmpfs 与共享内存速度对比: PHP 进程将高频读写的临时数据放在 /dev/shm(tmpfs)与使用 shmop 共享内存段 先说结论 Linux 内核里shmopSystemVSHM底层实际上也是 tmpfs内核内部挂了个 shmfs本质是 tmpfs 的变体。所以两者的物理页来源是一样的差距在访问路径。---访问路径对比/dev/shm 用文件操作PHPfile_get_contents(/dev/shm/x)→syscall(read)→VFS层 → tmpfs inode → page cache 里找页 → memcpy内核缓冲区 →PHP用户空间 每次读写都要用户态→内核态→用户态一次内存拷贝。 单次 syscall 开销~100–300ns高频调用累积很明显。---shmopshmat 之后直接访问shmget()/shmat()→ 内核在进程VA空间建一个VMA→ 指向 shmfs 的物理页PHPshmop_read()/shmop_write()→ userspace memcpy没有 syscall → 直接操作映射好的物理页 数据操作阶段没有模式切换就是个内存拷贝。---内核页面分配路径/dev/shm 文件写入时do_fault()→tmpfs_fault()→alloc_page()buddy allocator → 页插入 page cachexarray 结构 →PTE指向这页 shmget 段第一次访问时do_fault()→shmem_fault()← 同一个函数 →alloc_page()同一个 buddy allocator → 页插入 shmfs 的 address_space →PTE指向这页 shmem_fault 和 tmpfs_fault 在内核里是同一套代码mm/shmem.c页分配路径没有本质区别。---TLB影响 正常情况没有页被回收 两者 mmap/shmat 之后都是 进程A的页表:VA0x7f...→PPN0x4a200(TLB缓存这条)进程B的页表:VA0x7f...→PPN0x4a200(各自TLB缓存)进程A写数据 →PTE没变 → 不需要TLBshootdown → 进程B读到的是新数据同一物理页。 正常读写不触发TLBshootdown两者一样。TLBshootdown 触发时机 ┌────────────────────────────┬───────────────────────────────────────────────┬──────────────┐ │ 事件 │/dev/shm │ shmop │ ├────────────────────────────┼───────────────────────────────────────────────┼──────────────┤ │ 内存压力页被 kswapd 回收 │ 会触发需要 unmap 所有进程的PTEIPI广播 │ 同样会触发 │ ├────────────────────────────┼───────────────────────────────────────────────┼──────────────┤ │ 进程 detach/munmap │ 触发本地TLBflush │shmdt()同样 │ ├────────────────────────────┼───────────────────────────────────────────────┼──────────────┤ │ 权限变更 mprotect │ 触发 │ 触发 │ └────────────────────────────┴───────────────────────────────────────────────┴──────────────┘ 两者TLBshootdown 行为几乎相同因为物理页管理是同一套。 shmop 能做但/dev/shm 不好做的大页// FFI 调用申请 2MB 大页的 SHM 段$SHM_HUGETLB0x0800;$key$ffi-ftok(/tmp/k,a);$id$ffi-shmget($key,64*1024*1024,0644|$SHM_HUGETLB|0x0200);用2MB 大页hugepage覆盖64MB 数据 普通4KB 页64MB/4KB16384条TLB记录2MB 大页64MB/2MB32条TLB记录TLB压力直接降500倍对高频访问大块共享数据意义很大。/dev/shm 也支持大页但要 mount 时加 hugealways不灵活。---PHP代码对比?php// ── 方案1/dev/shm 文件操作最慢每次 syscall──file_put_contents(/dev/shm/cache,$data);$datafile_get_contents(/dev/shm/cache);// ── 方案2shmop无 syscall 数据访问──$keyftok(__FILE__,x);$shmshmop_open($key,c,0644,65536);shmop_write($shm,str_pad($data,65536,\0),0);// userspace memcpy$outrtrim(shmop_read($shm,0,65536),\0);// userspace memcpyshmop_close($shm);// shmop_delete($shm); // 段会持续存在直到删除或重启// ── 方案3APCuPHP 进程内最快序列化自动处理──apcu_store(key,$data,60);$dataapcu_fetch(key);---实际差异总结 ┌────────────┬──────────────────┬───────────────────┬─────────────────┐ │ │/dev/shm 文件IO│ shmop │ APCu │ ├────────────┼──────────────────┼───────────────────┼─────────────────┤ │ 底层内存 │ tmpfs page cache │ shmfs同 tmpfs │ mmap 匿名段 │ ├────────────┼──────────────────┼───────────────────┼─────────────────┤ │ 数据操作 │ syscallcopy │ userspace memcpy │ userspace 直接 │ ├────────────┼──────────────────┼───────────────────┼─────────────────┤ │TLB行为 │ mmap 后相同 │ 相同支持大页 │ 相同 │ ├────────────┼──────────────────┼───────────────────┼─────────────────┤ │ 跨进程 │ 文件路径 │ keyipcs 可见 │ 同一PHP进程组 │ ├────────────┼──────────────────┼───────────────────┼─────────────────┤ │PHP序列化 │ 手动 │ 手动 │ 自动 │ └────────────┴──────────────────┴───────────────────┴─────────────────┘ 高频读写选 APCu需要跨异构进程比如PHPPython 共享选 shmop/dev/shm 文件操作适合低频或大文件临时存储。

相关新闻