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. 核心优势是无数据拷贝,核心缺陷是需手动实现同步。

适用场景

  • 高频、大数据量的进程间通信(如视频流、实时数据传输);
  • 需低延迟的场景(如工业控制、金融交易系统)。
相关推荐
EmbedLinX2 小时前
嵌入式Linux之U-Boot
linux·服务器·笔记·学习
程序设计实验室2 小时前
从挖矿木马入侵到 Docker Rootless 加固,我的服务器安全复盘
linux·docker
雷电法拉珑2 小时前
财务数据批量采集
linux·前端·python
Roc.Chang3 小时前
Vite 启动报错:listen EACCES: permission denied 0.0.0.0:80 解决方案
linux·前端·vue·vite
暴力求解3 小时前
Linux进程(六)命令行参数
linux·运维·服务器
我怎么又饿了呀4 小时前
Linux 下 的Vim/Vi 操作指南
linux·运维·vim
nudt_qxx6 小时前
CUDA编程模型与硬件执行层级对应关系
linux·人工智能·算法
开开心心就好7 小时前
免费轻量电子书阅读器,多系统记笔记听书
linux·运维·服务器·安全·ddos·可信计算技术·1024程序员节
RisunJan7 小时前
Linux命令-lvreduce (收缩逻辑卷空间)
linux·运维·服务器