📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》 🎥 更多学习视频请关注 B 站:嵌入式Jerry
mmap 全面解析:原理、用法与实战
mmap(内存映射)是 Unix/Linux 下进行高效文件操作、进程间通信和内存管理的强大工具。它不仅能让文件操作变得像操作内存一样简单,还能支持共享内存、内存分配等高级场景。
一、什么是 mmap?
mmap(memory map)是 Linux 系统调用,能把文件或设备的内容映射到进程的虚拟内存空间,使我们像操作普通内存一样访问文件或外设数据。
mmap 的作用
高效文件访问:直接在内存中修改、读取文件内容,无需多余的读写系统调用。共享内存:支持多个进程间共享同一段物理内存,提升进程间通信效率。大块内存分配:适用于分配大于 malloc 限制的内存块,灵活扩展程序能力。硬件寄存器映射:驱动开发、嵌入式应用中,直接操作物理地址。
二、mmap 的核心原理
mmap 返回一个虚拟内存地址(虚拟空间),实际内容和底层文件或设备相关联。所有操作系统都通过页表(Page Table)管理虚拟地址与物理地址的映射。mmap 分配的内存空间与传统的“堆”与“栈”区域分离,由内核管理,不受 malloc 和 free 控制。
三、mmap 基本用法
mmap 原型
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:期望映射的起始地址(通常设为 NULL 让系统自动分配)length:映射内存区域大小(字节数,常为页对齐大小)prot:访问权限(如 PROT_READ, PROT_WRITE)flags:映射方式(如 MAP_SHARED, MAP_PRIVATE, MAP_ANONYMOUS)fd:被映射文件描述符(若是匿名映射则为 -1)offset:文件起始偏移(通常为 0)
四、典型实战代码
1. 文件映射示例
下面代码把一个文本文件映射到内存,直接修改文件内容:
#include
#include
#include
#include
#include
int main() {
const char *filepath = "test.txt";
int fd = open(filepath, O_RDWR | O_CREAT, 0666);
if (fd < 0) { perror("open"); return 1; }
// 保证文件有足够空间
ftruncate(fd, 4096);
// 映射文件到内存
char *map = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) { perror("mmap"); close(fd); return 1; }
// 写入内容
strcpy(map, "Hello, mmap! 内存映射很高效。\n");
msync(map, 4096, MS_SYNC); // 确保同步到磁盘
printf("文件内容:%s\n", map);
munmap(map, 4096);
close(fd);
return 0;
}
运行后,test.txt 会直接被改写,且不需要常规的 write 操作。
2. 匿名内存分配
无需文件,直接分配内存区域:
void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) { /* 错误处理 */ }
strcpy((char*)ptr, "匿名映射内存示例");
puts((char*)ptr);
munmap(ptr, 4096);
3. 物理内存/设备寄存器映射(嵌入式常用)
例如访问物理地址,需 root 权限(以 /dev/mem 为例):
int fd = open("/dev/mem", O_RDWR | O_SYNC);
void *reg = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x3F200000);
// 直接操作 reg 指向的物理区域
munmap(reg, 4096); close(fd);
(一般只在驱动开发或嵌入式底层使用)
五、mmap 常见问题与解答
Q1:mmap 分配的是堆空间吗?
不是。
mmap 分配的空间既不属于“堆”也不属于“栈”,而是进程虚拟地址空间中的独立区域。
Q2:mmap 能直接访问物理内存吗?
普通情况不能。
只有映射 /dev/mem、设备节点等时,才会直接访问物理内存,多用于驱动/硬件操作。
Q3:mmap 为什么效率高?
避免了内核与用户空间频繁拷贝,文件内容直接映射到进程空间,读写像内存操作一样高效。系统只在需要时加载相应内存页,提升性能和资源利用率。
Q4:mmap 用于共享内存怎么做?
多个进程 mmap 同一个文件或匿名共享区域(使用 MAP_SHARED),即可实现数据同步和进程间通信。
Q5:mmap 分配的内存如何释放?
用 munmap(addr, length) 释放。注意不要越界访问已解除映射的内存。
六、使用 mmap 的注意事项
同步问题
如果用 MAP_SHARED 映射,需要 msync 保证内存和文件同步。 多进程同步
多进程同时访问时需加锁或使用原子操作。 大块内存分配
mmap 适合大内存(如 128KB 以上),小内存用 malloc 更合适。 安全性
避免非法指针操作和释放后访问(悬挂指针问题)。 跨平台兼容性
Windows 等平台也有 mmap 类似机制(如 MapViewOfFile),但接口和用法略有不同。
七、结语与建议
mmap 适合用于大文件高效访问、进程间共享、内存映射外设等场景。使用 mmap 能让你的程序充分发挥 Linux 虚拟内存的威力,写出更高效和现代的系统代码。实际开发时,关注同步、权限、异常处理等细节,避免越界和资源泄漏问题。
推荐练习
尝试用 mmap 映射一个大文件,统计文件中的某个字符出现次数。试试用匿名 mmap 分配一块内存,实现父子进程通信。编写一个只读 mmap 工具,实现文件零拷贝快速读取。
希望这篇博文让你彻底掌握 mmap 的原理与实践!如需深入进阶代码、性能分析或跨平台用法,欢迎留言交流!
📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》 🎥 更多学习视频请关注 B 站:嵌入式Jerry