20251230 - 为什么Linux驱动开发中必须要用到ioremap来访问硬件?

为什么Linux驱动开发中必须要用到ioremap来访问硬件?

在驱动程序里面为什么不能直接用物理地址来访问寄存器?

这是一个非常深刻且本质的问题,触及了 操作系统(OS)裸机(Bare Metal)开发 最核心的区别。

一句话回答:因为 Linux 开启了 MMU(内存管理单元),CPU 发出的所有地址都会被当做"虚拟地址"处理。如果你直接用物理地址,CPU 会迷路,甚至导致系统崩溃。


1. "裸机"思维 vs "操作系统"思维

在单片机(STM32 / 51 / Arduino)上:

确实是直接用物理地址的。

C 复制代码
// STM32 代码
#define GPIO_BASE  0x40020000 
*(volatile int *)GPIO_BASE = 1; // 这是一个物理地址,CPU 直接把数据写到了物理线路

这是因为单片机通常没有 MMU,或者没开启 MMU。CPU 发出的地址直接对应内存条或外设的物理引脚。也就是:所见即所得。

在 Linux 系统上(I.MX6ULL):

Linux 启动后,做的第一件大事就是 开启 MMU。

一旦 MMU 开启,CPU 的"眼睛"就被换了一副眼镜。CPU 再也看不到真实的物理地址了,它只能看到虚拟地址。


2. 为什么要开启 MMU?(把事情搞这么复杂)

Linux 作为一个多任务系统,必须开启 MMU,主要为了两个目的:

  1. 权限保护(安全)
    • 防止 APP A 修改 APP B 的内存。
    • 防止 APP 直接修改内核的代码。
    • 如果直接用物理地址,恶意程序知道你的密码存在内存条的第 100 号单元,它直接去读第 100 号单元就窃取了。有了 MMU,APP 只能看到虚拟地址,根本接触不到物理 RAM。
  2. 地址隔离(多任务)
    • 每个 APP 都觉得自己拥有 4GB 的内存空间(虚拟的)。
    • MMU 负责把这些碎片化的物理内存拼凑成连续的虚拟内存给 APP 用。

3. 如果你强行用物理地址会发生什么?

假设寄存器的 物理地址 是 0x0209C000。

你在驱动代码里写:

C 复制代码
unsigned int *p = (unsigned int *)0x0209C000;
*p = 0x1;

CPU 的执行过程如下:

  1. 指令发出 :CPU 看到指令要访问地址 0x0209C000
  2. MMU 拦截 :由于 MMU 开启了,CPU 硬件会自动把这个地址当做 虚拟地址
  3. 查表失败 :MMU 去查"页表"(Page Table,也就是映射表),想看看虚拟地址 0x0209C000 对应哪个物理地址。
    • 但是,你并没有建立过这个映射!
    • 或者,这个虚拟地址范围可能已经被分配给内核代码段或者其他地方了。
  4. 系统崩溃
    • MMU 发现查不到,或者权限不对。
    • MMU 抛出一个 Page Fault(缺页异常)
    • 内核捕获异常,发现是非法访问,直接触发 Kernel Panic,系统死机,打印 "Unable to handle kernel paging request"。

4. ioremap 到底做了什么?

ioremap 就是 "给物理地址办签证" 的过程。

  • 输入 :物理地址 0x0209C000

  • 动作

    1. 内核在 内核虚拟地址空间 (通常是 3GB~4GB 的高端内存区域)里,找一段没用的空闲范围(比如 0xF0001000)。

    2. 内核修改 页表(Page Table),写入一条记录:

      "虚拟地址 0xF0001000 对应 物理地址 0x0209C000"

  • 返回 :把这个虚拟地址 0xF0001000 返回给你。

之后你操作 0xF0001000

  1. 你写 *virt_addr = 1
  2. CPU 拿到虚拟地址 0xF0001000
  3. MMU 查表,发现对应物理地址 0x0209C000
  4. 硬件信号准确传达到 LED 寄存器。

5. 图解对比

裸机模式 (Physical Access):

复制代码
CPU (地址 0x2000) ------------> 物理总线 (0x2000) ----> 硬件寄存器

Linux/MMU 模式 (Virtual Access):

复制代码
CPU (写 0xF000) ----> [MMU] ----(查表: 0xF000->0x2000)----> 物理总线 (0x2000) ----> 硬件寄存器

总结

  • 不能直接用的原因:开启 MMU 后,CPU 硬件机制决定了所有指令中的地址都会被解读为虚拟地址。直接用物理地址会被指引到错误的内存位置或导致异常。
  • ioremap 的作用:它是连接"虚拟世界"(代码)和"物理世界"(硬件)的桥梁,负责建立映射关系。

这就是为什么嵌入式 Linux 驱动开发比单片机麻烦一点的原因------你必须遵守操作系统的内存管理规则。

相关推荐
三伏5221 天前
stm32f103系列手册IIC笔记2
笔记·stm32·嵌入式硬件
changzehai1 天前
Rust + VSCode + probe-rs搭建stm32-rs嵌入式开发调试环境
vscode·后端·stm32·rust·嵌入式·probe-rs
猫猫的小茶馆1 天前
【Linux 驱动开发】三. 应用程序调用驱动过程分析
linux·arm开发·驱动开发·stm32·单片机·嵌入式硬件·pcb工艺
chengpei1471 天前
Arduino环境下开发STM32
stm32·单片机·嵌入式硬件
Embers(余烬矿)1 天前
STM32 usb 设备描述符失败
stm32·单片机·嵌入式硬件
zd8451015001 天前
CubeMX H743 lwip ETH初始化流程
网络·stm32·单片机
兆龙电子单片机设计1 天前
【STM32项目开源】STM32单片机智能温控风扇系统
stm32·单片机·物联网·开源·自动化
云山工作室1 天前
基于单片机的自动门控制系统设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计
橙露1 天前
工业控制嵌入式开发:Modbus 协议在 STM32 中的实现与调试
服务器·网络·stm32
想放学的刺客1 天前
单片机嵌入式试题(第21期)嵌入式系统启动异常排查与多任务同步机制设计两个核心方向,涵盖硬件调试、软件架构等综合能力考察。
stm32·单片机·嵌入式硬件·物联网·51单片机·proteus