mmap共享内存不是开箱即用的缓存通信方案,因其缺乏同步机制导致cpu缓存、编译器重排序和内存屏障问题,必须配合原子操作、严格对齐和跨进程安全的数据结构才能可靠使用。

为什么 mmap 共享内存不是“开箱即用”的缓存通信方案
直接用 mmap + MAP_SHARED 映射同一文件或匿名内存,多个进程确实能读写同一块物理页——但这只是“共享地址空间”,不是“共享缓存语义”。没有同步机制时,一个进程写完 buffer[0] = 1,另一个进程可能永远读不到 1,原因包括 CPU 缓存未刷新、编译器重排序、以及缺乏内存屏障。这不是 bug,是现代 CPU 和编译器的正常行为。
常见错误现象:read() 返回旧值、while (flag == 0) {} 死循环不退出、结构体部分字段更新但其他字段仍是随机值。
- 必须搭配内存序控制:用
std::atomic包裹关键标志位(如就绪标志、版本号),不能裸用int或bool - 避免跨进程直接访问复杂对象(如
std::string、std::vector)——它们内部指针指向各自进程堆,一解引用就段错误 - 匿名映射(
MAP_ANONYMOUS)在 fork 后子进程可继承,但多进程独立启动时必须用命名文件或/dev/shm/xxx路径做锚点
怎么安全地定义跨进程可见的数据结构
结构体必须是 trivially copyable,且所有字段需对齐、无 padding 陷阱,否则不同编译器或 ABI 下偏移错位。别依赖 sizeof(MyStruct) 自动对齐——它可能因调试宏或打包指令变化。
使用场景:高频小数据交换(如传感器采样点、状态快照),而非大块 blob 传输。
- 显式用
alignas(64)对齐到 cache line,避免 false sharing(比如两个进程频繁修改相邻字段,导致同一 cache line 在 CPU 间反复失效) - 用
static_assert(std::is_trivially_copyable_v<mystruct>)</mystruct>编译期卡住非平凡类型 - 字段全部用固定宽整型:
int32_t而非int,uint8_t[256]而非char buf[256] - 示例结构体:
struct SharedHeader { alignas(64) std::atomic<uint64_t> version{0}; std::atomic<bool> ready{false}; uint32_t data_len; uint8_t payload[4096]; }; </bool></uint64_t>注意:
version和ready放最前并强制对齐,确保它们独占 cache line,不和payload共享munmap 之后还能不能靠指针继续读?
不能。一旦任一进程调用
munmap,该进程内该地址范围立即失效,再访问就是Segmentation fault。更隐蔽的问题是:即使没munmap,进程 crash 或 exit 时内核自动清理映射,但另一进程还拿着野指针瞎读。性能影响:频繁
mmap/munmap开销远大于复用映射——每次系统调用都触发页表更新和 TLB 刷新。- 初始化阶段一次性
mmap,生命周期内全程复用;不要为每次通信新建映射 - 用
msync(MS_ASYNC)控制脏页刷盘时机(仅对文件映射有意义),对匿名映射无效 - 检查映射是否成功:必须判
if (addr == MAP_FAILED),不能只看是否为nullptr(MAP_FAILED是(void*)-1) - 文件映射时,确保 backing file 已
ftruncate()到足够大小,否则写越界触发SIGBUS
Linux 下 /dev/shm vs 普通文件映射选哪个
/dev/shm是 tmpfs,纯内存文件系统,无磁盘 IO,延迟低;普通文件映射则受存储介质拖累,但支持持久化。二者在mmap接口上完全一致,区别只在 backing store。容易踩的坑:
/dev/shm默认大小常为 64MB,超限会mmap失败并报ENOMEM,而不是磁盘满的ENOSPC。- 查当前限制:
df -h /dev/shm或读/proc/meminfo中Shmem:行 - 临时扩容:
sudo mount -o remount,size=2G /dev/shm - 若需跨重启存活,必须用普通文件(如
/tmp/cache.bin),并确保unlink()时机正确——最后一个进程退出前删,否则残留垃圾 - 权限控制:用
shm_open()配合fchmod()设定0660,比直接 open 文件更符合 POSIX 共享内存习惯
真正难的从来不是映射本身,而是让两个进程对“此刻数据是否有效”达成共识——这需要原子操作、内存序、以及对硬件缓存行为的基本敬畏。写错一个
memory_order_relaxed,就可能让通信变成玄学。 - 初始化阶段一次性
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!








