Files
2026-03-22 17:40:44 +08:00

307 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
这三个问题正好卡在“虚拟内存—总线—中断控制器”的交界处,我直接给你一个工程级、可运行在脑子里的模型。
---
# 一、DMA 用的是虚拟地址还是物理地址?
**短答DMA 面向的是“物理侧地址”,不是普通的 CPU 虚拟地址。**
但在现代系统里会多一层:**I/O 虚拟地址IOVA**。
---
## 1最基础模型无 IOMMU
```text
CPU 虚拟地址(VA) ──页表──> 物理地址(PA)
DMA 直接使用 PA
```
流程:
1. 驱动在内核里分配一块内存(这时它有 VA 和对应的 PA
2. 驱动把**物理地址 PA**写给设备DMA 描述符里)
3. 设备按这个 PA 直接读/写内存
👉 结论:
```text
DMA 不认识进程的虚拟地址VA只认物理地址PA
```
---
## 2现代模型有 IOMMU
引入一个关键组件IOMMU
```text
CPU VA ──> PA
DMA IOVA ──IOMMU──> PA
```
此时流程变成:
1. 驱动向 IOMMU 注册一段内存映射
2. 得到一个 **IOVA给设备用的“虚拟地址”**
3. 设备用 IOVA 发起 DMA
4. IOMMU 翻译成真实物理地址 PA
👉 关键好处:
```text
✔ 安全:设备不能乱访问内存
✔ 灵活:可以映射不连续的物理页
✔ 虚拟化支持多VM
```
---
## 3一句话总结
```text
无 IOMMUDMA 用物理地址
有 IOMMUDMA 用 IOVA再映射到物理地址
```
---
# 二、DMA 会不会和 CPU 冲突?能不能同时工作?
**短答:可以完全并行,但会“竞争资源”,不是互斥。**
---
## 1并行性关键认知
```text
CPU执行指令
DMA走总线搬数据
```
👉 两者是**独立硬件单元**
```text
✔ 可以同时工作
✔ 不需要互相等待
```
---
## 2那会不会冲突——会但在“带宽层面”
冲突发生在这里:
```text
内存控制器 / 总线(如 PCIe
```
多个参与者:
```text
CPUload/store
DMA设备
GPU显存访问
```
都要访问内存:
```text
→ 需要仲裁arbitration
```
---
## 3真实情况
```text
CPU读写内存 ←→ DMA读写内存
内存控制器调度
```
结果:
```text
✔ 可以并行
✔ 但会抢带宽(变慢)
❌ 不是“谁工作另一个就停”
```
---
## 4一个直觉类比
```text
内存 = 公路
CPU = 小汽车
DMA = 卡车
✔ 可以同时跑
✔ 但车多会堵
```
---
# 三、DMA 如何让 CPU 产生中断?
这个是很多人最模糊的一点,我们拆到“硬件信号级”。
---
## 1关键DMA 本身“不发中断”
👉 非常重要:
```text
DMA ≠ 中断源
设备(或控制器)才是
```
---
## 2完整路径标准 PC 架构)
```text
设备/NIC/USB控制器
发中断信号MSI / MSI-X / 旧IRQ
中断控制器APIC
CPU
```
---
## 3现代方式MSIMessage Signaled Interrupt
不用传统“中断线”,而是:
```text
设备发一个“特殊的内存写”
```
具体:
```text
设备(通过 PCIe
写入某个固定地址APIC寄存器
```
```text
这次“内存写” = 中断信号
```
---
## 4流程串起来DMA + 中断)
```text
1设备 DMA 写数据到内存 buffer
2设备决定
- 立刻中断?
- 延迟coalescing
3如果要中断
设备发 MSI本质是一次内存写
4APIC 接收到
→ 通知 CPU
5CPU
- 保存上下文
- 跳转中断处理函数
```
---
## 5为什么设计成“内存写触发中断”
好处:
```text
✔ 不需要专用中断线(可扩展)
✔ 支持多核可以指定哪个CPU
✔ 和DMA同一套总线PCIe
```
---
# 四、把三件事彻底统一(你要形成这个模型)
---
## 1地址体系
```text
CPUVA → PA
DMAIOVA → PA或直接 PA
```
---
## 2执行关系
```text
CPU 执行程序
DMA 搬数据
→ 完全并行
```
---
## 3通知机制
```text
DMA完成只是数据好了
设备决定是否发中断
MSI → APIC → CPU
```
---
# 五、一个完整“工业级时间线”(你可以脑内跑)
```text
t0驱动配置 DMA bufferIOVA/PA
t1设备收到数据
t2DMA → 写入内存
t3CPU 正在跑别的程序(完全并行)
t4设备决定
“现在通知CPU”
t5发 MSI一次PCIe内存写
t6APIC → CPU
CPU 被打断
t7中断处理函数
从 buffer 读数据
```
---
# 六、最后给你三句“压缩理解”
```text
1DMA 只负责“搬数据”,不负责“通知”
2地址上DMA 不用 CPU 的虚拟地址(除非经 IOMMU 转换)
3CPU 和 DMA 并行运行,只在“内存带宽”上竞争
```
---
如果你想再往下走一步,下一层是非常关键的:
* cache coherenceDMA 写内存CPU cache 怎么一致?)
* zero-copy 为什么难(缓存 + 映射问题)
* DPDK 为什么要关 cache / 绑核
这些是“性能优化”的真正核心。