Linux进程通讯——共享内存

共享内存是 Linux 进程间通信(IPC)中效率最高的方式,核心是让多个进程直接访问同一块物理内存区域,无需内核中转数据拷贝。本手册涵盖核心概念、关键函数、使用流程、实战示例和注意事项,适合零基础到进阶学习。

一、核心概念理解

1. 共享内存的本质

  • 定义 :内核管理的一块物理内存区域,通过 mmap 映射到多个进程的虚拟地址空间,进程读写该虚拟地址即操作同一块物理内存。
  • 核心优势:无数据拷贝(管道 / 消息队列需 2 次拷贝,共享内存仅 0 次),是最快的 IPC 方式。
  • 核心缺陷:无内置同步 / 互斥机制,多进程读写需配合信号量、互斥锁等保证数据安全。

2. 共享内存文件的特殊性

  • shm_open 创建的共享内存是 /dev/shm 目录下的内存文件(tmpfs 文件系统),非磁盘文件;
  • 可通过 ls /dev/shm 查看、cat /dev/shm/xxx 读取内容,系统重启后自动消失;
  • 权限体系复用文件权限(如 0666),支持用户 / 组级别的访问控制。

二、核心函数详细解析

POSIX 共享内存依赖以下核心函数(需包含头文件 #include <sys/mman.h>,编译时链接 rt 库:-lrt)。

1. shm_open ():创建 / 打开共享内存对象

函数原型
复制代码
int shm_open(const char *name, int oflag, mode_t mode);
参数说明
参数 作用
name 共享内存名称,必须以 / 开头 (如 /my_shared_memory),全局唯一;仅允许一个 /,不能嵌套(如 /my/shm 非法)
oflag 打开标志:- O_CREAT:不存在则创建;- O_RDWR:读写权限;- O_EXCL:与 O_CREAT 配合,名称已存在则报错
mode 权限位(如 0666),仅 O_CREAT 时有效;遵循 Linux 文件权限规则(r=4, w=2, x=1)
返回值
  • 成功:返回文件描述符(fd)(整型,用于后续操作);
  • 失败:返回 -1,设置 errno(如 EEXIST:名称已存在,EINVAL:名称格式非法)。
核心作用

创建 / 打开内核中的共享内存对象,在 /dev/shm 生成对应的内存文件(但此时大小为 0,需 ftruncate 分配物理内存)。

2. ftruncate ():设置共享内存大小

函数原型
复制代码
int ftruncate(int fd, off_t length);
参数说明
参数 作用
fd shm_open 返回的文件描述符
length 共享内存大小(字节),建议按内存页大小(4KB)对齐(如 4096、8192)
返回值
  • 成功:0;失败:-1(如 EBADF:fd 无效,EINVAL:长度非法)。
核心作用
  • 内核根据 length 为共享内存对象分配物理内存页;
  • /dev/shm 下的内存文件大小会更新为 length,是 mmap 映射的前提(无此步骤映射会失败)。

3. mmap ():映射共享内存到进程虚拟地址空间

函数原型
复制代码
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明
参数 作用
addr 指定映射的虚拟地址,通常传 NULL(让系统自动分配)
length 映射大小(需与 ftruncate 设置的大小一致)
prot 内存保护标志:- PROT_READ:可读;- PROT_WRITE:可写;- `PROT_READ PROT_WRITE`:读写
flags 映射类型(核心!):- MAP_SHARED:修改同步到物理内存(所有进程可见);- MAP_PRIVATE:修改仅当前进程可见(私有拷贝,无共享意义)
fd shm_open 返回的文件描述符
offset 映射偏移量,通常为 0(从开头映射)
返回值
  • 成功:返回映射后的虚拟地址指针
  • 失败:返回 MAP_FAILED(需判断,而非 NULL),设置 errno
核心作用

建立进程虚拟地址 ↔ 共享物理内存的映射关系,进程可通过该虚拟地址直接读写物理内存。

4. munmap ():解除映射关系

函数原型
复制代码
int munmap(void *addr, size_t length);
参数说明
参数 作用
addr mmap 返回的虚拟地址指针
length 映射大小(需与 mmap 一致)
返回值
  • 成功:0;失败:-1(如 EINVAL:地址 / 长度非法)。
核心作用
  • 断开当前进程虚拟地址与物理内存的映射,进程再读写该地址会触发段错误(SIGSEGV);
  • 释放进程虚拟地址空间的页表项,但物理内存需等所有进程解除映射后才释放。

5. close ():关闭共享内存文件描述符

函数原型
复制代码
int close(int fd);
参数说明
参数 作用
fd shm_open 返回的文件描述符
返回值
  • 成功:0;失败:-1(如 EBADF:fd 已关闭)。
核心作用
  • 释放当前进程持有的共享内存「操作句柄」,内核减少该对象的引用计数;
  • 仅关闭 fd,不删除共享内存对象,也不释放物理内存。
函数原型
复制代码
int shm_unlink(const char *name);
参数说明
参数 作用
name 共享内存名称(与 shm_open 一致,如 /my_shared_memory
返回值
  • 成功:0;失败:-1(如 ENOENT:名称不存在,EACCES:权限不足)。
核心作用
  • 立即删除 /dev/shm 下的内存文件,后续进程无法通过 shm_open 打开该对象;
  • 标记对象为「待销毁」,当所有进程执行 munmap + close 后,内核释放物理内存;
  • rm /dev/shm/xxx 效果完全等价(编程用 shm_unlink,手动清理用 rm)。

三、关键注意事项

1. 核心顺序(不可颠倒)

复制代码
创建/打开 → 设置大小 → 映射 → 读写 → 解除映射 → 关闭fd → 删除对象
  • munmapclose:避免 fd 关闭后仍访问映射地址;
  • closeshm_unlink:遵循文件操作的「先关后删」原则;
  • 多进程场景:子进程仅执行「解除映射 + 关闭 fd」,父进程最后执行「删除对象」。

2. 同步问题

  • 共享内存无内置同步,多进程同时读写会导致「脏数据」;
  • 解决方案:配合 POSIX 信号量(sem_open/sem_wait/sem_post)或互斥锁。

3. 资源清理

  • 进程异常退出时,需确保执行 shm_unlink(可通过 atexit 注册清理函数);
  • 残留的共享内存可手动删除:rm /dev/shm/xxx

4. 常见错误排查

错误现象 原因 解决方案
shm_open 失败 名称格式非法(无 / 开头) 名称必须以 / 开头(如 /my_shm
mmap 返回失败 未执行 ftruncate 设置大小 先调用 ftruncate 再映射
段错误(SIGSEGV) 读写超出共享内存大小 / 已解除映射 检查大小、确保映射未解除
shm_unlink 失败 传文件描述符而非名称字符串 shm_open 用的名称字符串

四、总结

个人理解:

首先通过 shm_open() 函数创建/打开一个共享内存对象(该对象是真实存在于 /dev/shm 目录的内存文件);

然后通过 ftruncate() 函数设置该共享内存的大小(内核据此分配物理内存);

再通过 mmap() 将该共享内存映射为进程可操作的虚拟地址;

通过在进程里面对该虚拟地址进行读写操作,实现多进程间的数据共享;

读写操作完成后,先通过 munmap() 函数断开该虚拟地址与共享物理内存的映射关系;

然后调用 close() 函数关闭 shm_open() 返回的文件描述符;

最后使用 shm_unlink() 函数删除 /dev/shm 下的共享内存文件,当所有进程都完成映射解除和文件描述符关闭后,内核释放共享内存占用的物理内存。

核心知识点

  1. 共享内存是「物理内存 + 内核抽象对象 + 进程虚拟地址映射」的组合体;
  2. shm_open 创「名」,ftruncate 分「实」,mmap 建「映射」;
  3. munmap 断「进程与内存的通路」,shm_unlink 删「共享内存的名字」;
  4. 核心优势是无数据拷贝,核心缺陷是需手动实现同步。

适用场景

  • 高频、大数据量的进程间通信(如视频流、实时数据传输);
  • 需低延迟的场景(如工业控制、金融交易系统)。
相关推荐
HLC++12 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室13 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy15 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕16 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫17 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生17 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1119 小时前
linux基础IO
linux·运维·服务器
BelongPanda21 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯21 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983421 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器