news 2026/4/18 5:27:12

Linux应用与驱动开发:mmap和内存映射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux应用与驱动开发:mmap和内存映射

学习笔记:Linux 驱动开发之mmap与内存映射

1. 核心概念:什么是mmap

mmap(Memory Map) 是一种内存映射文件的方法。在嵌入式 Linux 驱动开发中,它主要用于将外设的物理地址(如 GPIO 寄存器)映射到用户进程的虚拟地址空间

  • 传统方式:用户态read/write<–> 内核态拷贝数据 <–> 驱动操作寄存器。
  • mmap 方式:用户态指针 <–> MMU 直接转换 <–> 硬件寄存器。(零拷贝,速度极快

2. 核心难点:用户虚拟地址 vs 内核虚拟地址

这是理解 Linux 内存管理的关键分水岭。

2.1 对比图表

特性用户虚拟地址 (UVA)内核虚拟地址 (KVA)
定义应用程序看到的地址。操作系统内核驱动看到的地址。
地址范围 (32位)0x00000000~0xBFFFFFFF(0~3G)0xC0000000~0xFFFFFFFF(3G~4G)
可见性私有。进程A和进程B的0x1000互不相干。全局共享。所有进程进入内核态后看到的都是同一份。
映射工具mmap()ioremap()
访问权限仅当前进程有效,进程结束即销毁。系统启动即存在,只有内核代码可读写。
物理对应通常不连续,按需分配(缺页中断)。逻辑地址线性映射,vmalloc/ioremap非线性。

2.2 它们与物理地址的关系

假设 i.MX6ULL 的一个 LED 寄存器物理地址是0x0209C000

  1. CPU/MMU:只认物理地址0x0209C000
  2. 驱动程序 (KVA):通过ioremap拿到一个地址(如0xF0001000)。驱动写0xF0001000-> MMU 查表 -> 写入物理地址。
  3. 应用程序 (UVA):通过mmap拿到一个地址(如0xB7001000)。应用写0xB7001000-> MMU 查表 -> 写入物理地址。

结论:UVA 和 KVA 就像是通往同一个房间(物理地址)的两扇不同的门。一扇门开在“用户公寓”(0-3G),另一扇门开在“管理员办公室”(3-4G)。


3.mmap的实现流程(驱动与应用配合)

3.1 应用程序做了什么?

应用程序中的mmap最终会调用驱动中实现的.mmap接口(即下面的my_driver_mmap函数)

int fd = open("/dev/my_led", O_RDWR); // 申请映射:从 offset 0 开始,映射 4096 字节 unsigned char *addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); // 直接操作硬件! *addr = 0x01; // 点灯

3.2 驱动程序做了什么? (fops.mmap)

驱动的核心任务是构建页表

  1. 获取物理地址:知道你要操作哪个寄存器(例如0x0209C000)。
  2. 计算页帧号 (PFN):Linux 内存管理以“页”为单位(通常 4KB)。
    • PFN = 物理地址 >> PAGE_SHIFT(即除以 4096)。
  3. 调用remap_pfn_range:这是核心函数。它负责修改当前进程的页表,建立 UVA 到 物理地址 的映射。
// 驱动代码示例 static int my_driver_mmap(struct file *file, struct vm_area_struct *vma) { unsigned long phy_addr = 0x0209C000; // 硬件物理地址 // 关键:设置为不使用缓存 (Nocache)!否则寄存器读写会出错 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); // 建立映射:把 vma->vm_start (应用层的UVA) 映射到 phy_addr (物理地址) if (remap_pfn_range(vma, vma->vm_start, // 用户虚拟地址起始 phy_addr >> PAGE_SHIFT, // 物理地址页帧号 vma->vm_end - vma->vm_start, // 映射大小 vma->vm_page_prot)) // 属性:无缓存 { return -EAGAIN; } return 0; }

4. 关键细节与注意事项

4.1 为什么要pgprot_noncached

  • 原因:对于硬件寄存器,必须禁止 CPU 缓存 (Cache)
  • 后果:如果不加这一行,应用程序写数据可能只写到了 Cache 里,LED 灯根本不会亮,或者读取的状态是旧的。

4.2 为什么映射大小通常是 4KB (4096)?

  • MMU 管理内存的最小单位是一页 (Page),ARM Linux 默认页大小是 4096 字节。
  • 即使你只需要操作 4 个字节的寄存器,mmap也会映射整整一页(4096 字节)。因此,你在计算偏移量时要小心。

4.3ioremapmmap的关系

  • 驱动程序自己要访问寄存器 -> 用ioremap
  • 驱动程序想让应用程序直接访问寄存器 -> 实现.mmap接口(内部调用remap_pfn_range)。
  • 通常一个驱动里这两个都会用到。

5. 总结

  1. 物理地址是唯一的真理,但被 CPU 藏在了 MMU 后面。
  2. KVA (内核虚拟地址)是驱动在内核态用的,通过ioremap映射。
  3. UVA (用户虚拟地址)是应用在用户态用的,通过mmap系统调用请求,由驱动协助映射。
  4. remap_pfn_range是连接 UVA 和 物理地址 的桥梁。

下一步实践建议:

在韦东山的开发板上,你可以写一个简单的程序,利用 /dev/mem 这个系统自带的驱动节点。它已经实现了 mmap 功能。你可以尝试用 mmap 映射 GPIO 的物理基地址,然后在应用层直接点亮 LED,这将是你理解这一概念最直观的实验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 22:16:58

Makefile中打印变量

在Makefile中打印变量有多种方法&#xff0c;以下是常用的几种方式&#xff1a;1. 使用 $(info) 函数makefile # 示例1&#xff1a;直接打印 VAR hello world $(info VAR $(VAR))# 示例2&#xff1a;带说明的打印 $(info [DEBUG] VAR $(VAR))# 示例3&#xff1a;在规则外部打…

作者头像 李华
网站建设 2026/4/17 12:55:43

Figma中文界面终极解决方案:5分钟完成专业设计工具全面汉化

Figma中文界面终极解决方案&#xff1a;5分钟完成专业设计工具全面汉化 【免费下载链接】figmaCN 中文 Figma 插件&#xff0c;设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma英文操作界面而困扰&#xff1f;想要快速掌握这款国际…

作者头像 李华
网站建设 2026/4/11 19:20:51

软件I2C多器件总线管理策略:深度剖析

软件I2C多器件总线管理&#xff1a;从原理到实战的系统性设计在嵌入式开发的世界里&#xff0c;你有没有遇到过这样的窘境&#xff1f;MCU上唯一的硬件I2C接口已经被OLED屏占用&#xff0c;而新加入的温湿度传感器和加速度计也非要走I2C——引脚不够、地址冲突、通信时断时续……

作者头像 李华
网站建设 2026/4/17 17:29:06

【系统架构师备考笔记】006 电子政务角色关系

一、核心角色定位政府定义&#xff1a;主导者与服务提供者职责政策制定与平台运营在线服务交付&#xff08;如税务申报 $T\int_{a}^{b} r(t)dt$&#xff09;数据安全管理技术特点政务云平台支撑服务数字化率 $\eta \geq 95%$公民定义&#xff1a;主要受益者与参与者职责使用在线…

作者头像 李华