跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何在 Go 中利用 mmap 实现跨进程的数据同步

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
清理旧对象,避免残留。

相关文章