共享内存是linux ipc中效率最高的方式,需五步完成:获取键值、创建/获取段、映射地址、同步读写、清理资源;必须配合信号量等同步机制,否则引发数据竞争。

共享内存是 Linux 进程间通信(IPC)中效率最高的一种方式,核心在于让多个进程映射到同一块物理内存区域,从而实现零拷贝、直接读写。它不自动提供同步机制,因此实际使用必须搭配信号量或互斥锁等手段,否则极易出现数据竞争。
共享内存的关键步骤
整个流程分五步,缺一不可:
-
获取唯一键值(key):通常用
ftok("/path/to/file", 'a')生成,确保不同进程能协商出同一个 key;也可用IPC_PRIVATE创建私有段(仅限父子进程) -
创建或获取共享内存段:调用
shmget(key, size, IPC_CREAT | 0666),size 必须是系统页面大小(如 4096)的整数倍 -
映射到进程地址空间:用
shmat(shmid, NULL, 0)将内存段挂载到当前进程的虚拟地址,返回指针可直接读写 -
读写与同步:通过返回的指针操作数据,但任何并发访问都需自行加锁(例如配合
sem_wait()/sem_post()) -
清理资源:用
shmdt(shmaddr)断开映射;由一个进程调用shmctl(shmid, IPC_RMID, NULL)彻底删除,否则残留段会占用系统资源
常见错误与规避方法
实践中容易踩坑的地方集中在键值、权限和生命周期管理上:
- 多个进程用不同 key 访问——结果各自创建独立段,看似“共享”实则无效;务必统一 key 来源(推荐
ftok+ 同一路径文件) - 权限位写成
0644却未设IPC_CREAT——导致shmget失败返回 -1;创建时至少带IPC_CREAT,获取已有段才可省略 - 只调用
shmdt而忘记shmctl(..., IPC_RMID, ...)——共享内存段仍驻留在内核中,可用ipcs -m查看,用ipcrm -m shmid手动清理 - 映射后未检查返回值是否为
(void*)-1——shmat失败时返回该值,直接解引用会导致段错误
父子进程 vs 非亲缘进程
使用场景决定 key 和生命周期的设计:
-
父子进程通信:父进程创建共享内存后
fork(),子进程继承 shmid,直接调用shmat;可用IPC_PRIVATE简化 key 管理,避免全局命名冲突 -
任意进程通信:必须依赖一致的 key(如
ftok("/tmp/shm.key", 'A')),且需约定好谁负责最终删除;建议用文件锁或临时文件协调初始化顺序 - 注意:
shmat和shmdt是每个进程独立操作;一个进程断开不影响其他进程继续使用,直到被shmctl删除
调试与验证技巧
快速确认共享内存是否按预期工作:
- 运行
ipcs -m查看当前所有共享内存段,关注key、shmid、perms和lpid(最后操作进程 PID) - 用
strace -e trace=shmget,shmat,shmdt,shmctl ./your_program跟踪系统调用,确认各步骤是否成功执行 - 在共享内存中写入带时间戳的字符串(如
sprintf(shm, "written by %d at %ld", getpid(), time(NULL))),再由另一进程读取验证可见性 - 若数据始终不更新,检查是否漏掉
shmdt后又重复shmat——每次shmat都返回新地址,旧指针失效











