虚拟地址与物理地址: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与地址转换流程
咱们画个图,看看数据怎么流。
流程其实很简单:
- 设备发起DMA请求,携带IOVA
- IOMMU截获请求,查页表
- 找到对应的物理地址
- 访问真正的物理内存
- 数据返回给设备
你想想看,这跟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(客户机物理地址),不是真正的机器物理地址。
解决方案:
- VMM(虚拟机监视器)为虚拟机创建IOMMU页表
- 页表把GPA映射到HPA(宿主机物理地址)
- GPU发DMA时,IOMMU自动完成GPA→HPA转换
- 虚拟机完全感知不到底层地址映射
这就是IOMMU在虚拟化中的核心价值——让设备以为自己在直接访问物理内存,实际上IOMMU在中间做了地址翻译。
7. 避坑指南
我曾经踩过的坑:
- IOVA范围不要和系统保留地址冲突(比如PCIe MMIO区域)
- 大页(2MB/1GB)能提升TLB命中率,但分配时要确保物理连续性
- 多设备共享IOMMU时,注意页表隔离,别让设备A读到设备B的数据
- IOMMU TLB刷新的开销很大,频繁映射/解映射会影响性能
说白了,IOMMU就是个给设备用的MMU。理解了CPU的虚拟地址转换,IOMMU的概念就很好上手。区别在于:CPU管进程,IOMMU管设备。
好了,这一章的内容就到这里。记住三个核心:IOVA、页表、地址转换。下次我们聊聊IOMMU的硬件实现细节。