mmap + shm_open 是 Linux 下最轻量可控的跨进程共享内存方案,需用 POSIX 共享内存对象、ftruncate 设置大小、MAP_SHARED 映射、[[gnu::packed]] 对齐结构体、sem_init 在共享内存中初始化信号量,并用 fchmod 绕过 umask 权限干扰。

用 mmap + shm_open 是最轻量且可控的方案
Linux 下 C++ 跨进程共享内存,mmap 配合 shm_open 是事实标准——它不依赖第三方库,内核原生支持,零拷贝,延迟最低。比 boost::interprocess 更透明,比 System V shmget 更现代、权限更清晰。
关键点在于:共享对象必须是 POSIX 共享内存对象(/my_shm 这种路径名),不能直接 mmap 普通文件或匿名内存来跨进程通信(除非用 MAP_SHARED + fork,但那不算“跨进程”)。
-
shm_open创建/打开一个命名共享内存对象,返回 fd;必须指定O_RDWR,否则mmap会失败 -
ftruncate必须调用,设置大小;没这步,mmap会映射 0 字节区域,读写必段错误 - 映射时用
MAP_SHARED——MAP_PRIVATE只写时复制,改了也不通知对方 - 进程退出不自动销毁共享内存;要用
shm_unlink(通常由创建方在确认所有使用者都退出后调用)
结构体布局要加 [[gnu::packed]] 或手动对齐
两个进程可能用不同编译器、不同 ABI、甚至不同优化等级编译,结构体默认对齐会导致同一块内存被解释成完全不同的字段位置——这是最隐蔽的崩溃源。
比如一个 struct { int a; char b; },在进程 A 里占 8 字节(a 占 4,b 占 1,后面补 3 字节对齐),但在进程 B 里若对齐方式不同,读 b 就会越界或错位。
立即学习“C++免费学习笔记(深入)”;
- 强制紧凑布局:
struct [[gnu::packed]] Msg { int a; char b; }; - 或用
alignas(1)+ 显式填充字段,更跨平台 - 绝对不要依赖
sizeof自动计算;双方必须用完全一致的定义头文件(建议用#pragma once+static_assert校验sizeof(Msg) == 5) - 浮点字段尤其危险——x86-64 和 ARM 对
float的二进制表示虽一致,但若一方用了-ffast-math,结果可能不一致
sem_wait / sem_post 比自旋锁更省 CPU,但得配 sem_init 在共享内存里
共享内存本身不提供同步机制。裸写 flag = 1 然后轮询读,看似简单,实则浪费 CPU、不可靠、还容易因编译器重排失效。
POSIX 信号量能放在共享内存中(sem_init(&sem, 1, 0) 第二个参数为 1 表示“在共享内存中”),是唯一不用额外 IPC 通道就能协同的方案。
- 信号量对象(
sem_t)必须分配在mmap出来的内存里,不能是栈或堆变量 - 初始化后,任意进程调用
sem_post,另一方sem_wait就能阻塞等待,无忙等 - 别用
pthread_mutex_t—— 它不能跨进程(即使PTHREAD_PROCESS_SHARED也要求所有进程用同一 libc 实例,实际部署极难保证) - 如果需要超时等待,用
sem_timedwait,别自己写 while + usleep
注意 shm_open 的权限掩码和 umask 干扰
shm_open("/log_buf", O_CREAT | O_RDWR, 0666) 看似给了全权限,但实际创建的 shm 对象权限可能是 0600——因为当前进程的 umask 默认是 0022,会屏蔽掉写权限。
后果:另一个非同一用户、甚至同一用户但不同 session(如 systemd service)启动的进程,shm_open 会失败并报 Permission denied。
- 临时解决:启动前
umask 0,但不推荐(影响其他文件) - 正解:显式调用
fchmod(fd, 0666),绕过 umask - 更稳妥:用
open("/dev/shm/xxx", ...)替代shm_open(Linux 特有,但行为更可预测) - 调试时用
ls -l /dev/shm/看真实权限,别只信代码里的0666
跨进程共享内存真正难的不是映射,而是让双方对“这块内存现在能不能读/写/是否有效”达成一致——同步、对齐、权限,三者漏一,运行时就静默出错。











