POSIX共享内存最轻量可控方案是shm_open创建命名对象(名以/开头)后ftruncate设大小,再mmap配MAP_SHARED映射;需用__attribute__((packed))对齐结构体、sem_open或PTHREAD_PROCESS_SHARED互斥量同步,并由主控进程最后shm_unlink。

共享内存用 shm_open + mmap 最直接
Linux 下 C++ 进程间共享内存,最轻量、最可控的方式就是 POSIX 共享内存:先用 shm_open 创建/打开一个命名共享内存对象,再用 mmap 映射到进程地址空间。它不依赖文件系统路径(但名字需以 / 开头),也不经过内核缓冲区,读写就是直接内存访问。
常见错误是忘记设权限或没调 ftruncate:
-
shm_open的flags至少要带O_RDWR,创建时加O_CREAT - 必须在
mmap前对 fd 调用ftruncate(fd, size),否则映射后访问越界会触发SIGBUS - 名字如
"/my_shm"不能含下划线以外的特殊字符,且长度受限(通常 ≤ NAME_MAX-4)
结构体布局必须用 packed 避免对齐差异
多个进程共用一块内存,如果结构体成员因编译器默认对齐导致偏移不同,读写就会错位。尤其当进程用不同编译器、不同优化等级、甚至不同架构(x86_64 vs aarch64)时,风险极高。
解决方法是显式控制内存布局:
立即学习“C++免费学习笔记(深入)”;
- 用
__attribute__((packed))(GCC/Clang)或#pragma pack(1)(MSVC 兼容写法) - 避免使用
std::string、std::vector等动态对象——它们内部指针指向各自进程堆,另一方无法解析 - 推荐只放 POD 类型:
int、char[256]、uint64_t,必要时用union做类型双关(注意严格别名规则)
示例定义:
struct __attribute__((packed)) ShmHeader {
uint32_t version;
uint32_t data_len;
char payload[1];
};
同步必须靠外部机制,共享内存本身不带锁
共享内存只是“大家能看见同一块字节”,读写冲突、ABA 问题、伪共享、缓存不一致全得自己管。别指望 mmap 自动同步 CPU 缓存——它只是映射,不是原子操作。
实际可用的同步方式有:
- 用
sem_open创建命名信号量,配合sem_wait/sem_post控制临界区 - 用
pthread_mutex_t+PTHREAD_PROCESS_SHARED属性(需放在共享内存内,且初始化只能由第一个进程做) - 无锁编程(如环形缓冲区 + 原子序号)适合高性能场景,但调试成本高,
std::atomic在共享内存中可用,但需确保对齐和大小一致
注意:std::mutex 不能直接放共享内存里——它是进程内对象;必须用 pthread_mutex_t 并显式设置共享属性。
shm_unlink 不是立即释放,要等所有进程都 munmap
很多人以为调了 shm_unlink("/my_shm") 就立刻销毁内存,结果发现另一进程还能读写。这是因为 POSIX 共享内存遵循“引用计数”语义:shm_unlink 只是删掉名字,真正释放要等到所有进程都 munmap 且关闭了对应 fd。
典型陷阱:
- 父进程创建 shm 后 fork 子进程,父子都继承了 fd,但只在父进程调
shm_unlink→ 子进程仍可访问,且退出时不自动清理 - 某个进程崩溃未
munmap,该 shm 会一直残留(可见于/dev/shm/),下次启动可能因名字冲突失败 - 调试时用
ipcs -m看不到 POSIX shm,得用ls /dev/shm/
稳妥做法:约定一个“主控进程”负责最后 shm_unlink,其他进程只 shm_open + mmap + munmap,不碰 shm_unlink。


















