mmap + shm_open 才能跨进程共享命名内存,因为 POSIX 共享内存对象(如 /dev/shm/ 下)具有统一内核标识和缓存一致性保障,而普通文件 mmap 各自映射独立页表且无同步机制。

为什么 mmap + shm_open 才能跨进程共享命名内存
直接用普通文件 mmap 无法实现“命名共享内存”——因为每个进程打开的文件路径虽然一样,但映射的是各自独立的页表项,且内核不保证缓存一致性;真正支持多进程协同访问的,是 POSIX 共享内存对象,它由 shm_open 创建、mmap 映射,底层走的是 /dev/shm/(或 tmpfs),有统一的内核对象标识。
实操建议:
-
shm_open返回的 fd 必须用PROT_READ | PROT_WRITE和MAP_SHARED调用mmap,否则写入不会同步到其他进程 - 名字必须以
/开头(如"/my_shm"),不能含其他斜杠,否则shm_open失败并返回-1,errno为EINVAL - 首次创建需传
O_CREAT | O_EXCL防止覆盖;后续打开只需O_RDWR,否则可能因权限不足失败 - 映射前记得用
ftruncate设置大小,否则mmap会失败并报errno = EINVAL
shm_open 失败常见原因和对应检查点
不是所有系统默认启用 POSIX 共享内存,也不是所有用户都有权限操作 /dev/shm。错误往往卡在第一步,而不是映射逻辑。
常见错误现象与排查:
立即学习“C++免费学习笔记(深入)”;
-
shm_open返回-1,errno == ENOENT:说明/dev/shm未挂载,运行mount -t tmpfs tmpfs /dev/shm(需 root) -
errno == EACCES:当前用户对/dev/shm无写权限,检查ls -ld /dev/shm,必要时chmod 1777 /dev/shm -
errno == EMFILE:进程已打开太多文件,调大ulimit -n或关闭未用 fd - 名字含非法字符(如空格、反斜杠、非 ASCII)也会导致
EINVAL,严格限定为 ASCII 字母、数字、下划线、短横线,且首字符为/
多进程同步必须自己加锁,mmap 不提供原子性保障
共享内存只是把一块物理内存暴露给多个进程,读写本身完全不加锁。哪怕只改一个 int,也可能因 CPU 缓存未刷新、指令重排或非原子写入(比如 64 位值在 32 位平台分两次写)导致数据错乱。
实操建议:
- 不要依赖
volatile解决同步问题——它只禁用编译器优化,不控制 CPU 缓存或内存序 - 优先用
std::atomic(C++11+)包装共享变量,如std::atomic<int>* counter = static_cast<:atomic>*>(ptr);</:atomic></int> - 若需复杂结构或临界区,必须配
pthread_mutex_t存放在共享内存中,并用PTHREAD_PROCESS_SHARED初始化(注意:Windows 不支持该 flag) - 避免用
std::mutex—— 它内部指针指向进程私有堆,跨进程无效
进程退出后共享内存不会自动释放,必须显式清理
POSIX 共享内存对象生命周期独立于进程,即使所有进程都退出了,只要没被 shm_unlink,对象就一直存在,占用内存且名字被占用,下次 shm_open 带 O_CREAT | O_EXCL 就会失败。
关键操作顺序:
- 最后一个使用该共享内存的进程,应在退出前调用
shm_unlink("/my_shm");仅此而已,不需要先munmap(但建议先做) -
shm_unlink不会立即释放内存,而是等所有fd关闭、所有mmap解除后才真正销毁 - 调试时可手动清理:
ls /dev/shm查看残留对象,rm /dev/shm/my_shm直接删除(前提是没进程映射着,否则会报Device or resource busy) - 如果程序异常崩溃,没机会调
shm_unlink,建议起始时先尝试shm_unlink一次(忽略ENOENT),再shm_open,作为“清理-重建”惯用法
命名共享内存的坑不在映射本身,而在对象生命周期管理、同步语义和系统配置这三个地方。少一个环节,程序就可能跑几天才出错,而且很难复现。



















