虚拟地址与物理地址:IOMMU/SMMU的核心概念

大家好,我是你们的讲师。今天我们来聊聊IOMMU和SMMU最基础、也最核心的概念——地址转换。说实话,我刚开始接触这块时,也被各种地址绕得头晕。但别担心,咱们一步步拆解。

1. 为什么需要地址转换?

先问个问题:没有IOMMU的时候,DMA是怎么工作的?

传统DMA直接使用物理地址。设备要读写内存,CPU告诉它「你去物理地址0x1000读数据」。设备照做,简单粗暴。

但问题来了——

  • 安全问题:设备可以访问任意物理内存。恶意设备能偷看其他进程的数据。
  • 碎片问题:物理内存是连续的,但分配大块连续内存越来越难。
  • 虚拟化困境:虚拟机看到的物理地址是假的(GPA),设备直接访问会出错。

嗯,这就是IOMMU登场的原因。它给设备也加了一层「虚拟地址」的概念。

2. IO虚拟地址(IOVA)是什么?

IOVA,全称是IO Virtual Address。说白了,就是设备看到的地址空间。

我习惯这么理解:

  • CPU侧:虚拟地址(VA)→ 物理地址(PA)
  • 设备侧:IO虚拟地址(IOVA)→ 物理地址(PA)

IOMMU负责把IOVA翻译成真正的物理地址。设备只管发IOVA,剩下的IOMMU搞定。

关键点:IOVA不一定是物理地址的别名。它可以是任意地址,只要IOMMU能正确映射。

我在项目中遇到过一种情况:某款网卡驱动分配DMA缓冲区时,总是申请连续的物理内存。系统跑久了,内存碎片化严重,分配失败。后来改用IOVA,把分散的物理页映射成连续的IOVA空间,问题就解决了。

3. DMA与地址转换流程

咱们画个图,看看数据怎么流。

设备 (网卡/GPU等) IOMMU/SMMU 地址转换单元 页表缓存(TLB) 物理内存 IOVA 物理地址 数据返回 DMA地址转换流程 设备发出IOVA → IOMMU查页表 → 得到物理地址 → 访问内存 整个过程对设备透明,设备只认IOVA

流程其实很简单:

  1. 设备发起DMA请求,携带IOVA
  2. IOMMU截获请求,查页表
  3. 找到对应的物理地址
  4. 访问真正的物理内存
  5. 数据返回给设备

你想想看,这跟CPU的MMU做虚拟地址转换,是不是很像?

4. 页表与页表项

IOMMU的页表结构和CPU页表类似,但有一些特殊之处。

4.1 页表结构

典型的IOMMU页表是多级结构。以ARM SMMU为例,支持4级页表:

级别 名称 粒度 覆盖范围
L0 页全局目录(PGD) 整个地址空间
L1 页上级目录(PUD) 大块区域
L2 页中间目录(PMD) 2MB(大页) 中等区域
L3 页表(PTE) 4KB(标准页) 小区域

多级页表的好处是节省内存。如果只用一级页表,4GB地址空间需要4MB页表(每个PTE 4字节)。多级页表可以按需分配,用多少建多少。

4.2 页表项(PTE)格式

一个典型的页表项包含这些字段:

// ARM SMMU 页表项示例(64位)
Bit 63:51  - 保留/扩展属性
Bit 50:48  - 权限控制(读/写/执行)
Bit 47:12  - 物理页帧号(PFN)
Bit 11:9   - 内存属性(缓存策略等)
Bit 8      - 访问标志(AF)
Bit 7      - 脏页标志(DBM)
Bit 6:5    - 页表类型(块/页/表)
Bit 4      - 用户/特权访问
Bit 3      - 可写
Bit 2      - 可读
Bit 1      - 非安全访问
Bit 0      - 有效位

嗯,这里要注意:有效位(Bit 0)是最关键的。如果为0,IOMMU会触发缺页异常。我曾经调试过一个bug,设备DMA一直超时,查了半天发现是页表项的有效位没置1。这种低级错误,犯过一次就记住了。

5. IOMMU页表 vs CPU页表

两者有很多相似之处,但也有区别:

  • 相同点:都做地址转换,都支持多级页表,都有TLB缓存
  • 不同点
    • CPU页表针对进程切换,IOMMU页表针对设备
    • IOMMU页表通常由内核管理,设备不直接操作
    • IOMMU支持更大的地址空间(有些实现支持48位甚至52位IOVA)

小技巧:调试IOMMU问题时,可以先用简单的1:1映射(IOVA=物理地址),排除页表配置错误。我经常这么干。

6. 实际应用场景

讲个真实案例。之前做虚拟化方案,虚拟机里跑GPU计算任务。GPU要做DMA,但虚拟机看到的地址是GPA(客户机物理地址),不是真正的机器物理地址。

解决方案:

  1. VMM(虚拟机监视器)为虚拟机创建IOMMU页表
  2. 页表把GPA映射到HPA(宿主机物理地址)
  3. GPU发DMA时,IOMMU自动完成GPA→HPA转换
  4. 虚拟机完全感知不到底层地址映射

这就是IOMMU在虚拟化中的核心价值——让设备以为自己在直接访问物理内存,实际上IOMMU在中间做了地址翻译。

7. 避坑指南

我曾经踩过的坑:

  • IOVA范围不要和系统保留地址冲突(比如PCIe MMIO区域)
  • 大页(2MB/1GB)能提升TLB命中率,但分配时要确保物理连续性
  • 多设备共享IOMMU时,注意页表隔离,别让设备A读到设备B的数据
  • IOMMU TLB刷新的开销很大,频繁映射/解映射会影响性能

说白了,IOMMU就是个给设备用的MMU。理解了CPU的虚拟地址转换,IOMMU的概念就很好上手。区别在于:CPU管进程,IOMMU管设备。

好了,这一章的内容就到这里。记住三个核心:IOVA、页表、地址转换。下次我们聊聊IOMMU的硬件实现细节。


专注资料整理