syscall.Mmap单独无法实现跨进程同步,因其映射普通文件时各进程物理页不一致;必须组合shm_open+mmap+POSIX信号量等跨进程同步原语,确保共享内存对象和同步机制均具内核级可见性。

不能用 syscall.Mmap 直接实现跨进程数据同步——它映射的是普通文件,内核不保证多进程间物理页一致;必须组合 shm_open + mmap + 跨进程同步原语,否则写入对另一进程不可见。
为什么 syscall.Mmap 单独调用无法同步
常见错误现象:syscall.Mmap 返回成功,进程 A 写了数据,进程 B 读到空值、旧值或乱码。
根本原因:普通文件(如 os.Open 打开的)被两个进程各自 mmap 后,Linux 不强制它们映射到同一块物理内存页。即使都用 MAP_SHARED,写操作只刷新本进程的页缓存,不触发跨进程可见性。
关键区别:
-
shm_open创建的是 POSIX 共享内存对象(内核级),所有打开它的进程共享同一 backing store -
os.Open打开的是普通文件,mmap后仍是独立的文件映射实例 -
syscall.Mmap本身不识别“共享内存语义”,它只忠实地按 fd 类型执行映射
必须用 shm_open + mmap 的完整流程
这是 Linux/macOS 上唯一可落地的路径。Windows 完全不支持 shm_open,需换方案(如命名管道或内存映射文件 via CreateFileMapping)。
进程 A(创建者):
- 调用
syscall.ShmOpen("/myshm", syscall.O_CREAT|syscall.O_RDWR, 0600) - 立刻
syscall.Ftruncate(fd, size)—— 必须在mmap前,否则mmap返回EINVAL - 再
syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
进程 B(加入者):
- 调用
syscall.ShmOpen("/myshm", syscall.O_RDWR, 0)—— 不带O_CREAT,否则可能覆盖 - 建议也
Ftruncate并校验大小,避免长度不一致导致越界访问 - 同样
Mmap,参数与 A 完全一致
示例关键片段(省略错误检查):
fd, _ := syscall.ShmOpen("/go_shm", syscall.O_CREAT|syscall.O_RDWR, 0600)
syscall.Ftruncate(fd, 4096)
data, _ := syscall.Mmap(fd, 0, 4096, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
同步机制不能靠 sync.Mutex 或 atomic
常见误用:sync.Mutex 字段含指针和 runtime 状态,放到 mmap 区域里会 panic;atomic.CompareAndSwapUint32 只在单进程地址空间内原子,跨进程完全无效。
正确做法:同步原语本身也得驻留在共享内存中,并由 OS 保证跨进程语义:
- POSIX 信号量:
sem_open("/mysem", syscall.O_CREAT, 0600, 1),然后sem_wait/sem_post - 自旋锁 +
atomic需手动实现,且必须用atomic.CompareAndSwapUint32操作共享内存中的 uint32 字段(该字段需对齐、初始化为 0) - 注意:sem_open 名称在 macOS 必须以
/开头、不能含多余斜杠;Linux 更宽松但需链接-lrt
容易被忽略的生命周期与清理细节
最常漏掉的三件事:
-
shm_unlink必须由最后一个使用者调用,且通常应在所有进程都munmap后 —— 提前 unlink 不影响已映射进程,但新进程无法再打开 -
syscall.Munmap是必须的,不调用会导致资源泄漏(尤其长期运行服务) - 进程异常退出时,共享内存对象不会自动销毁,需靠
shm_unlink显式清理,否则下次启动可能因残留对象而失败
实际部署中,建议用临时名称(如带 PID 或 UUID 后缀)+ 启动时尝试 shm_unlink 清理旧对象,避免残留。


















