当前位置: 首页 > news >正文

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,这将是你理解这一概念最直观的实验。

http://www.jsqmd.com/news/240858/

相关文章:

  • Day 34:【99天精通Python】单元测试 (Unittest) - 给代码上个保险
  • 强烈安利10个AI论文软件,MBA毕业论文轻松搞定!
  • Day 35:【99天精通Python】综合实战 - 爬虫与数据分析可视化(上) - 数据采集与入库
  • 多FDCAN接口同步配置实战:双通道并行通信实现
  • Figma中文界面本地化:设计师专属的语言解决方案
  • Day 36:【99天精通Python】综合实战 - 爬虫与数据分析可视化(下) - 让数据“说话“
  • 导师推荐!8个AI论文平台测评:研究生开题报告全攻略
  • Intel平台嵌入式SPI通信:新手教程
  • Doris与Trino集成:统一SQL大数据查询引擎
  • 大模型微调技术详解:从全参数微调到RLHF的演进与应用
  • 学Simulink——基础储能管理场景实例:基于Simulink的多时间常数储能配置优化仿真
  • Day 38:【99天精通Python】线程池与进程池 - 优雅地管理并发
  • “死了么”App爆火,我发现了个安卓版,代码开源!
  • Figma中文插件完全配置指南:告别英文界面困扰
  • 基于Simulink的多时间常数储能配置优化仿真
  • 基于深度学习的森林火灾识别系统(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)
  • 基于YOLOv8的小麦田间病害识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
  • 多语言界面在screen中的实现:项目应用
  • 基于Simulink的混合PO与INC切换MPPT策略仿真
  • 学长亲荐8个一键生成论文工具,专科生毕业论文必备!
  • RHEL9系统部署与Linux命令操作实验报告
  • 《嵌入式操作系统》_在ubuntu系统中使用wine环境安装source insight_20260113
  • STM32调试技巧:Keil MDK实用操作指南
  • AI架构的云原生设计:AI应用架构师如何利用云服务优化架构?
  • 大数据数据服务在物流行业的应用
  • AI智能体(Agent)全解析+代码示例
  • 大语言模型完整技术栈:从理论到实践的全面指南
  • 无需本地安装!Linux服务器上用WPS办公,还能远程访问?这招太实用了
  • 大模型本地化部署与微调实战指南:从入门到精通
  • 如何查看相册访问数据?看这里!