mmap 与 System V 共享内存对比
概述
mmap 和 System V 共享内存都是 Linux 中用于进程间通信(IPC)的机制, 两者都允许多个进程共享同一块物理内存, 实现零拷贝的高效通信. 本文档从使用方式、特点、实现机制等多个维度进行详细对比.
使用方式对比
API 对比
mmap 相关 API
#include<sys/mman.h>// 创建内存映射void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);// 取消映射intmunmap(void*addr,size_tlength);// 同步到文件intmsync(void*addr,size_tlength,intflags);特点:
- 统一的 API, 既可用于文件映射, 也可用于匿名映射
- 需要文件描述符(匿名映射时使用 -1)
- 直接返回虚拟地址指针
System V 共享内存 API
#include<sys/shm.h>// 创建/获取共享内存段intshmget(key_tkey,size_tsize,intshmflg);// 附加到进程地址空间void*shmat(intshmid,constvoid*shmaddr,intshmflg);// 分离共享内存intshmdt(constvoid*shmaddr);// 控制操作(删除、获取信息等)intshmctl(intshmid,intcmd,structshmid_ds*buf);特点:
- 专门的 IPC API, 需要多个步骤
- 使用键值(key)标识共享内存段
- 需要显式的 attach/detach 操作
使用流程对比
mmap 使用流程
文件映射方式:
// 1. 打开文件intfd=open("shared.dat",O_RDWR|O_CREAT,0666);ftruncate(fd,size);// 2. 创建映射void*addr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);// 3. 使用映射区域memcpy(addr,data,size);// 4. 同步(可选)msync(addr,size,MS_SYNC);// 5. 取消映射munmap(addr,size);close(fd);匿名映射方式:
// 1. 创建匿名映射void*addr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);// 2. 使用(父子进程通过 fork 共享)// 3. 取消映射munmap(addr,size);System V 共享内存使用流程
// 1. 生成键值key_tkey=ftok(".",'s');// 2. 创建/获取共享内存段intshmid=shmget(key,size,IPC_CREAT|0666);// 3. 附加到进程地址空间void*addr=shmat(shmid,NULL,0);// 4. 使用共享内存memcpy(addr,data,size);// 5. 分离共享内存shmdt(addr);// 6. 删除共享内存段(可选, 通常由一个进程负责)shmctl(shmid,IPC_RMID,NULL);标识方式对比
| 特性 | mmap | System V 共享内存 |
|---|---|---|
| 标识方式 | 文件路径 + 文件描述符 | 键值(key) + shmid |
| 键值生成 | 不需要(使用文件路径) | 需要ftok()或IPC_PRIVATE |
| 跨进程共享 | 通过文件路径 | 通过键值 |
| 持久性 | 文件映射持久, 匿名映射不持久 | 显式删除前持久 |
特点对比
功能特性对比
| 特性 | mmap | System V 共享内存 |
|---|---|---|
| 文件关联 | 可以映射文件, 也可以匿名映射 | 不关联文件, 纯内存 |
| 持久性 | 文件映射持久, 匿名映射不持久 | 显式删除前持久 |
| 同步到文件 | 支持msync()同步到文件 | 不支持文件同步 |
| 按需加载 | 支持, 利用虚拟内存按需加载 | 不支持, 一次性分配 |
| 大文件处理 | 适合处理大文件 | 不适合, 需要一次性分配全部内存 |
| 灵活性 | 高, 可以映射文件、设备、匿名区域 | 中, 只能创建纯内存段 |
| 地址选择 | 可以指定地址或让系统选择 | 可以指定地址或让系统选择 |
性能特点对比
| 性能指标 | mmap | System V 共享内存 |
|---|---|---|
| 零拷贝 | ✅ 是 | ✅ 是 |
| 直接内存访问 | ✅ 是 | ✅ 是 |
| 延迟 | 低(直接内存访问) | 极低(直接内存访问) |
| 吞吐量 | 高(零拷贝) | 极高(零拷贝) |
| CPU占用 | 低 | 低 |
| 内存占用 | 按需加载, 节省内存 | 一次性分配, 占用固定内存 |
| 缺页处理 | 支持, 按需加载文件内容 | 不支持, 内存已分配 |
同步机制需求
两者都需要用户空间的同步机制:
| 同步机制 | mmap | System V 共享内存 |
|---|---|---|
| 内核保护 | ❌ 无 | ❌ 无 |
| 需要同步 | ✅ 是 | ✅ 是 |
| 常用同步方式 | 信号量、互斥锁、原子操作 | 信号量、互斥锁、原子操作 |
| 同步复杂度 | 相同 | 相同 |
实现机制对比
内核实现对比
mmap 实现机制
核心数据结构:
vm_area_struct(VMA): 虚拟内存区域描述符mm_struct: 进程地址空间描述符file: 文件对象(文件映射时)
实现路径:
sys_mmap2 → do_mmap_pgoff → - 地址选择(get_unmapped_area) - VMA 创建和初始化 - 文件映射(file->f_op->mmap) 或匿名映射(shmem_zero_setup) - VMA 插入到进程地址空间关键特点:
- 基于虚拟内存管理(VMA)
- 文件映射使用页缓存(page cache)
- 匿名映射使用 shmem 文件系统
- 支持按需加载(缺页处理)
System V 共享内存实现机制
核心数据结构:
shmid_kernel: 共享内存段描述符shmem_inode_info: shmem 文件系统 inodekern_ipc_perm: IPC 权限和键值管理
实现路径:
sys_shmget → newseg → - 创建 shmid_kernel - 创建 shmem 文件(shmem_file_setup) - 安装到 IPC ID 表 sys_shmat → do_shmat → - 权限检查 - 调用 do_mmap_pgoff 映射 shm_file - 更新附加计数关键特点:
- 基于 IPC 框架(键值、权限管理)
- 内部使用 shmem 文件系统
- 最终通过 mmap 机制映射到进程地址空间
- 需要显式的 attach/detach 操作
内存管理对比
| 方面 | mmap | System V 共享内存 |
|---|---|---|
| 内存分配时机 | 按需分配(缺页时) | 创建时一次性分配 |
| 物理页管理 | 页缓存或匿名页 | shmem 文件系统页 |
| 换入换出 | 支持(除非 MAP_LOCKED) | 支持(除非 SHM_LOCK) |
| 内存锁定 | MAP_LOCKED 标志 | SHM_LOCK 命令 |
| 页表管理 | 通过 VMA 管理 | 通过 VMA 管理(内部使用 mmap) |
文件系统依赖
| 特性 | mmap | System V 共享内存 |
|---|---|---|
| 文件系统 | 文件映射依赖文件系统, 匿名映射使用 shmem | 内部使用 shmem, 用户不可见 |
| 文件可见性 | 文件映射时文件可见 | 文件不可见(内部使用) |
| 文件同步 | 支持 msync 同步到文件 | 不支持文件同步 |
适用场景对比
mmap 适用场景
✅适合:
- 大文件处理(按需加载)
- 需要文件持久化的场景
- 数据库、缓存等需要文件映射的应用
- 需要高效文件 I/O 的场景
- 父子进程通过 fork 共享内存
❌不适合:
- 简单的进程间通信(管道更合适)
- 不需要文件关联的纯内存共享
System V 共享内存适用场景
✅适合:
- 大数据量传输
- 对性能要求极高的场景
- 需要频繁通信的场景
- 不需要文件关联的纯内存共享
- 需要显式生命周期管理的场景
❌不适合:
- 需要文件持久化的场景
- 大文件处理(需要一次性分配全部内存)
- 需要按需加载的场景
优缺点对比
mmap 优缺点
优点:
- ✅ 功能强大: 既可以映射文件, 也可以匿名映射
- ✅ 按需加载: 利用虚拟内存机制, 节省内存
- ✅ 文件同步: 支持同步到文件, 数据持久化
- ✅ 大文件处理: 适合处理大文件, 无需一次性加载
- ✅ 灵活性高: 可以映射文件、设备、匿名区域
- ✅ 标准 POSIX API: 跨平台兼容性好
缺点:
- ❌ API 相对复杂: 需要理解虚拟内存概念
- ❌ 文件依赖: 文件映射依赖文件系统
- ❌ 匿名映射共享: 父子进程共享需要 fork, 独立进程共享需要文件
- ❌ 同步机制: 需要用户空间同步机制
System V 共享内存优缺点
优点:
- ✅ 性能极高: 零拷贝, 直接内存访问
- ✅ 简单直接: 专门的 IPC API, 语义清晰
- ✅ 持久性: 显式删除前一直存在
- ✅ 独立进程通信: 不要求进程间有亲缘关系
- ✅ 生命周期管理: 显式的创建、附加、分离、删除
缺点:
- ❌ 内存占用: 一次性分配全部内存, 不能按需加载
- ❌ 无文件关联: 不支持文件持久化
- ❌ 键值管理: 需要管理键值, 可能冲突
- ❌ 系统限制: 受系统对共享内存大小、数量限制
- ❌ 资源清理: 需要显式删除, 否则会一直占用
- ❌ 非 POSIX: System V 特有, 跨平台兼容性差
代码示例对比
相同功能的不同实现
场景: 多进程共享计数器
使用 mmap (文件映射):
// 创建共享文件intfd=open("counter.dat",O_RDWR|O_CREAT,0666);ftruncate(fd,sizeof(int));// 映射int*counter=mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);// 使用(*counter)++;// 清理munmap(counter,sizeof(int));close(fd);unlink("counter.dat");使用 System V 共享内存:
// 创建共享内存key_tkey=ftok(".",'c');intshmid=shmget(key,sizeof(int),IPC_CREAT|0666);// 附加int*counter=shmat(shmid,NULL,0);// 使用(*counter)++;// 清理shmdt(counter);shmctl(shmid,IPC_RMID,NULL);功能差异示例
mmap 独有: 文件映射和同步
// mmap 可以映射文件并同步intfd=open("data.txt",O_RDWR);void*addr=mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);// 修改数据strcpy(addr,"Hello");// 同步到文件msync(addr,4096,MS_SYNC);munmap(addr,4096);close(fd);System V 共享内存不支持文件映射和同步.
System V 共享内存独有: 显式生命周期管理
// System V 共享内存可以独立于进程存在key_tkey=ftok(".",'s');intshmid=shmget(key,4096,IPC_CREAT|0666);// 进程 A 使用void*addr1=shmat(shmid,NULL,0);// ... 使用 ...shmdt(addr1);// 进程 A 退出, 但共享内存段仍然存在// 进程 B 可以继续使用同一个共享内存段void*addr2=shmat(shmid,NULL,0);// ... 使用 ...shmdt(addr2);// 最后删除shmctl(shmid,IPC_RMID,NULL);mmap 文件映射虽然也持久, 但需要文件存在.
选择建议
选择 mmap 的情况
- ✅ 需要文件持久化
- ✅ 需要处理大文件(按需加载)
- ✅ 需要文件 I/O 和内存访问的统一接口
- ✅ 需要跨平台兼容性(POSIX 标准)
- ✅ 父子进程通过 fork 共享内存
选择 System V 共享内存的情况
- ✅ 纯内存共享, 不需要文件关联
- ✅ 需要显式的生命周期管理
- ✅ 需要独立于进程存在的共享内存
- ✅ 大数据量传输, 性能要求极高
- ✅ 不需要按需加载, 内存充足
两者都可以的情况
- 简单的进程间数据共享
- 需要零拷贝的高性能通信
- 多进程共享数据结构
在这种情况下, 可以根据具体需求选择:
- 如果需要文件持久化 → mmap
- 如果需要显式生命周期管理 → System V 共享内存
- 如果需要跨平台 → mmap
- 如果只需要纯内存共享 → System V 共享内存
总结
mmap 和 System V 共享内存都是高效的进程间通信机制, 两者都实现了零拷贝的直接内存访问. 主要区别在于:
- 功能定位: mmap 更通用(文件映射 + 匿名映射), System V 共享内存专门用于 IPC
- 内存管理: mmap 支持按需加载, System V 共享内存一次性分配
- 持久化: mmap 支持文件持久化, System V 共享内存不支持
- API 设计: mmap 统一 API, System V 共享内存专门的 IPC API
- 跨平台: mmap 是 POSIX 标准, System V 共享内存是 System V 特有
选择时应该根据具体需求: 需要文件持久化或大文件处理选择 mmap, 需要纯内存共享或显式生命周期管理选择 System V 共享内存.