Linux共享内存通过页表映射使多进程访问同一物理内存,无需内核拷贝,是System V IPC中最快方式;需配合信号量同步,用ftok生成唯一key,shmget/shmat/shmdt/shmctl管理生命周期。

Linux共享内存的核心在于让多个进程通过页表映射,访问同一块物理内存区域。它不经过内核拷贝数据,直接读写内存,因此是System V IPC中速度最快、开销最低的通信方式。但要注意:它本身不带同步能力,必须配合信号量或互斥锁使用,否则容易出现数据竞争。
共享内存怎么做到“多个进程看到同一块内存”
每个进程都有独立的虚拟地址空间和页表。操作系统在物理内存中划出一段区域,然后把这段物理地址分别映射到不同进程的页表中——通常挂载到进程地址空间的“共享区”(堆与栈之间)。这样,虽然各进程用各自的虚拟地址访问,最终都落在同一片物理内存上。
这个过程由内核完成,用户通过系统调用触发:
- shmget():申请物理内存 + 创建内核描述结构(shmid_ds),返回共享内存ID(shmid)
- shmat():将该内存段映射进当前进程的虚拟地址空间,返回可读写的指针
- shmdt():取消映射,仅断开进程与内存的关联,不释放物理内存
- shmctl():控制生命周期,如IPC_RMID删除整个共享内存段
key值不是随便填的,得靠ftok生成
key是共享内存的唯一标识,用于跨进程定位同一段内存。不能硬编码数字(易冲突),也不能由系统随机分配(别的进程无法得知)。标准做法是用ftok()函数生成:
- 第一个参数是**已存在的文件路径**(如/tmp/shm.key),必须真实存在且有读权限
- 第二个参数是**非零整数proj_id**(如0x66),相当于项目编号,建议用ASCII字符或小整数
- ftok内部用路径inode号和proj_id做简单运算,生成一个key_t类型值,重复概率极低
两个进程只要传入相同的路径和proj_id,就能得到同一个key,从而通过shmget拿到同一段共享内存。
创建与使用的典型流程(含关键标志位)
服务端(先创建)和客户端(后连接)需协调好顺序。常用组合如下:
- shmget(key, size, IPC_CREAT | 0644):若不存在则创建,存在则获取;0644设权限(类似文件)
- shmget(key, size, IPC_CREAT | IPC_EXCL | 0644):只创建,若已存在则失败(避免误连旧数据)
- shmat(shmid, nullptr, 0):让系统自动选择虚拟地址挂载;若需固定地址,第二个参数填目标地址(需对齐)
- 读写时像操作普通内存一样:int* data = (int*)shmat(...); *data = 123;
注意:size建议按页对齐(如4096字节倍数),内核实际分配以页为单位,小尺寸也会占满一页。
别忘了清理和排查
共享内存段一旦创建,就独立于进程生命周期存在。进程退出不会自动销毁它,残留会导致内存泄漏或下次启动失败。
- 用ipcs -m查看当前用户所有共享内存段(显示key、shmid、权限、大小、nattch连接数)
- 用ipcrm -m shmid手动删除指定段;或在代码中调用shmctl(shmid, IPC_RMID, nullptr)
- 如果shmat返回(void*)-1,常见原因:key错、权限不足、shmid无效、内存已满
实践时建议封装初始化/清理逻辑,确保每次运行前清除旧资源,避免调试时被历史残留干扰。










