mmap 全面解析:原理、用法与实战

📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry


mmap 全面解析:原理、用法与实战

mmap(内存映射)是 Unix/Linux 下进行高效文件操作、进程间通信和内存管理的强大工具。它不仅能让文件操作变得像操作内存一样简单,还能支持共享内存、内存分配等高级场景。


一、什么是 mmap?

mmap(memory map)是 Linux 系统调用,能把文件或设备的内容映射到进程的虚拟内存空间,使我们像操作普通内存一样访问文件或外设数据。

mmap 的作用

  • 高效文件访问:直接在内存中修改、读取文件内容,无需多余的读写系统调用。
  • 共享内存:支持多个进程间共享同一段物理内存,提升进程间通信效率。
  • 大块内存分配:适用于分配大于 malloc 限制的内存块,灵活扩展程序能力。
  • 硬件寄存器映射:驱动开发、嵌入式应用中,直接操作物理地址。

二、mmap 的核心原理

  • mmap 返回一个虚拟内存地址(虚拟空间),实际内容和底层文件或设备相关联。
  • 所有操作系统都通过页表(Page Table)管理虚拟地址与物理地址的映射。
  • mmap 分配的内存空间与传统的“堆”与“栈”区域分离,由内核管理,不受 mallocfree 控制。

在这里插入图片描述


三、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 <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

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 的注意事项

  1. 同步问题

    • 如果用 MAP_SHARED 映射,需要 msync 保证内存和文件同步。
  2. 多进程同步

    • 多进程同时访问时需加锁或使用原子操作。
  3. 大块内存分配

    • mmap 适合大内存(如 128KB 以上),小内存用 malloc 更合适。
  4. 安全性

    • 避免非法指针操作和释放后访问(悬挂指针问题)。
  5. 跨平台兼容性

    • Windows 等平台也有 mmap 类似机制(如 MapViewOfFile),但接口和用法略有不同。

七、结语与建议

  • mmap 适合用于大文件高效访问、进程间共享、内存映射外设等场景。
  • 使用 mmap 能让你的程序充分发挥 Linux 虚拟内存的威力,写出更高效和现代的系统代码。
  • 实际开发时,关注同步、权限、异常处理等细节,避免越界和资源泄漏问题。

推荐练习

  1. 尝试用 mmap 映射一个大文件,统计文件中的某个字符出现次数。
  2. 试试用匿名 mmap 分配一块内存,实现父子进程通信。
  3. 编写一个只读 mmap 工具,实现文件零拷贝快速读取。

希望这篇博文让你彻底掌握 mmap 的原理与实践!如需深入进阶代码、性能分析或跨平台用法,欢迎留言交流!


📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值