DMA 方式
DMA 是整章演化线的终点,也是唯一一种数据不经过 CPU 寄存器的方式。
7.3.2 末尾算出来的那个
理解 DMA 只需要抓住一件事:DMA 控制器在传送期间会变成总线主设备。 本章前面所有的接口都是从设备,只有它例外。而”成为主设备”这件事的全部代价,就是它必须和 CPU 争总线——三种争法就是本节的核心。
机制
DMA 控制器的组成
它同时具有两副面孔:对 CPU 是从设备(被配置),对主存是主设备(去读写)。
| 部件 | 作用 | 谁写入初值 |
|---|---|---|
| 主存地址寄存器 AR | 下一个要读写的主存地址 | CPU 预处理时 |
| 字计数器 WC | 还剩多少个字没传 | CPU 预处理时 |
| 数据缓冲寄存器 BR | 暂存正在传送的一个字 | 传送中自动 |
| DMA 请求触发器 | 设备准备好了一个字,置 1 | 设备 |
| 控制/状态逻辑 | 管理传送、修改 AR 和 WC、发 HRQ | —— |
| 中断机构 | 块传完后向 CPU 发中断 | —— |
AR 与 WC 的联动是本节最实用的一条:
有的教材让 WC 从
flowchart LR CPU["CPU"] -->|"① 预处理:写 AR、WC、方向"| DMAC["DMA 控制器"] DEV["外设"] -->|"② DREQ<br/>我准备好一个字"| DMAC DMAC -->|"③ HRQ<br/>总线请求"| CPU CPU -->|"④ HLDA<br/>总线响应,CPU 让出总线"| DMAC DMAC -->|"⑤ 直接发地址读写"| MEM["主存"] DEV <-->|"⑤ 数据"| MEM DMAC -->|"⑥ WC=0,中断请求"| CPU classDef cpu fill:#e3f2fd,stroke:#1565c0 classDef dmac fill:#ffcdd2,stroke:#b71c1c,stroke-width:3px classDef other fill:#c8e6c9,stroke:#1b5e20 class CPU cpu class DMAC dmac class DEV,MEM other
图上的第⑤步是全章唯一一条”设备 ↔ 主存”的直连数据通路。 数据不进 CPU 寄存器——这就是 DMA 与前两种方式的根本分界。
第③④步是 6.1.1 主设备判据的直接应用:DMA 控制器发出 HRQ 申请、CPU 用 HLDA 让出总线之后,发地址的人从 CPU 变成了 DMA 控制器,主设备身份随之易手。“谁发地址谁是主设备”这条判据在这里第一次派上真正的用场。
三种与 CPU 争总线的方式
这是本节的第一考点。三种方式的差别只在于”DMA 一次占用总线多久”。
| 停止 CPU 访存 | 周期挪用(周期窃取) | DMA 与 CPU 交替访存 | |
|---|---|---|---|
| DMA 一次占多久 | 整块传完才还 | 一个存取周期,传一个字就还 | 把一个 CPU 周期固定切成两半 |
| CPU 停顿 | 长,整块期间不能访存 | 短,只在被挪用的那个周期 | 不停顿,两者各用各的半周期 |
| 总线控制权切换 | 一次(开销小) | 每字一次(开销大) | 不需要申请,硬连线分时 |
| DMA 吞吐 | 最高 | 中 | 中 |
| 硬件复杂度 | 最低 | 中 | 最高 |
| 适用 | 高速设备、成批传送 | 最常用,兼顾两者 | 需要专门设计的总线控制逻辑 |
“交替访存”这一种要特别注意:它不需要申请和归还总线。 总线控制权的划分是预先硬连线定死的——前半周期归 CPU,后半周期归 DMA。所以它没有 HRQ/HLDA 这套握手,也就没有切换开销,代价是硬件必须专门设计。
这条与 6.1.1 的”分时”是同一个概念:交替访存就是把一个周期分时给两个主设备,而分时的划分方式是静态的(预先定死),不是动态仲裁的。
周期挪用的三种情形
“挪用”具体挪的是什么,取决于 CPU 当时在干什么。这是第二考点。
| CPU 此刻 | 结果 |
|---|---|
| 不访存(在做运算、译码) | DMA 直接用,完全没有冲突 |
| 正在访存 | DMA 等这个存取周期结束再用 |
| 同时要访存 | DMA 优先 |
第三行的理由必须说清楚:DMA 优先不是因为它重要,而是因为它等不起。
外设的数据是流式产生的——磁盘转过去了、网线上的比特过来了,如果 DMA 控制器不及时把数据搬走,缓冲寄存器就会被下一个数据覆盖,数据永久丢失。 而 CPU 晚一个周期访存只是慢一点,没有任何东西会丢。
“损失可恢复的让路给损失不可恢复的”——这是优先级设计的通用原则,与5.5.3 里”故障优先级高于外部中断”是同一条道理。
边界辨析:"周期窃取"窃的是什么周期
窃的是存取周期(总线周期),不是 CPU 的指令周期,更不是 CPU 的运算能力。
说法 判断 ”DMA 窃取了 CPU 的一个指令周期” ❌ “DMA 窃取了一个存取周期 / 总线周期” ✅ “DMA 期间 CPU 完全停止工作” ❌ CPU 照常执行不访存的操作 ”DMA 期间 CPU 一定被延迟” ❌ 如果 CPU 恰好不访存,延迟为 0 理解的关键是 CPU 并非每个周期都访存。 取指要访存,取操作数要访存,但译码、运算、写回寄存器都不访存。这些周期正是 DMA 可以”白捡”的。
这也解释了为什么 Cache 命中率高的系统里 DMA 的影响更小——Cache 挡掉了大部分访存请求,CPU 需要用主存总线的周期本来就少。
三个阶段
| 阶段 | 谁做 | 做什么 |
|---|---|---|
| 预处理 | CPU(软件) | 测试设备状态、写 AR / WC / 方向、启动设备 |
| 数据传送 | DMA 控制器(硬件) | 循环:请求总线 → 传一个字 → AR+1、WC−1,直到 WC=0 |
| 后处理 | CPU(软件,在中断服务程序里) | 校验数据、处理错误、决定是否继续传下一块、唤醒进程 |
首尾两段都是 CPU 做的——“DMA 不需要 CPU”这句话只在中间那一段成立。
后处理为什么必须有:DMA 控制器只会搬字节,它不知道传来的数据对不对、要不要重传、哪个进程在等它。这些判断只有软件能做。
DMA 中断与程序中断的两点不同
DMA 结束时也发中断,但这个中断和 7.3.2 的中断有两处实质差别:
| 程序中断 | DMA 结束中断 | |
|---|---|---|
| 目的 | 为了传送数据 | 数据已经传完了,只为后处理 |
| 频率 | 每个字一次 | 每个数据块一次 |
而 DMA 请求(DREQ/HRQ)与中断请求(INT)的差别更大,这是第三考点:
| 中断请求 INT | DMA 请求 HRQ | |
|---|---|---|
| 要求 CPU 做什么 | 暂停当前程序,转去执行服务程序 | 只借走总线,程序不用停 |
| 响应时机 | 一条指令结束后 | 一个总线周期(存取周期)结束后 |
| 要不要保存断点 | 要 | 不要(程序根本没中断) |
| 改变 CPU 现场吗 | 改变(PC、PSW、寄存器) | 不改变 |
| 响应更及时的是 | —— | DMA(等待粒度更细) |
“DMA 响应比中断响应更及时”的完整理由就在第二行:指令可能要好几个总线周期,而 DMA 只需等最小的那一个粒度。
再补一句,DMA 请求的优先级高于中断请求,理由与前面周期挪用一样:数据会丢,而中断可以等。
DMA 与 Cache 的一致性问题
DMA 绕过 CPU 直接读写主存,就绕过了 CPU 的私有 Cache,于是可能读到或留下旧数据。
两个方向各有一个故障:
| 方向 | 问题 | 原因 |
|---|---|---|
| DMA 读主存(内存 → 设备) | 设备读到旧数据 | CPU 刚写的新值还在 Cache 里(写回法下没写到主存) |
| DMA 写主存(设备 → 内存) | CPU 读到旧数据 | Cache 里还留着这块地址的旧副本 |
三种解法:
- 不可缓存:把 DMA 缓冲区标记为不进 Cache。简单,但 CPU 访问该区域会变慢。
- 软件维护:DMA 之前写回(flush)相关 Cache 行,DMA 之后作废(invalidate)它们。由驱动程序负责。
- 硬件一致性(总线侦听):Cache 控制器监听总线上的 DMA 事务,自动作废或更新——这就是 3.5.5 的侦听协议,只不过那里的另一方是别的 CPU 核,这里是 DMA 控制器。
认出这一点很省事:多核之间的 Cache 一致性和 DMA 引起的一致性,是同一个问题的两个来源,解法也是同一套。
性能计算
模板仍与前两节同形,只是”每次”的粒度变成了一整块。
已知:设备传输率
与中断方式的公式一模一样,差别全在
另一类常考的是”总线占用”,问的是另一回事:
这两个数字要分开答:
接上第 6 章的算例:6.1.5 算过总线的峰值带宽
边界
| 说法 | 判断 | 理由 |
|---|---|---|
| ”DMA 完全不需要 CPU” | ❌ | 预处理和后处理都是 CPU 做 |
| ”DMA 传送不产生中断” | ❌ | 块传完后要中断做后处理 |
| ”DMA 控制器始终是从设备” | ❌ | 传送期间是主设备;被 CPU 配置时才是从设备 |
| ”周期窃取窃的是 CPU 指令周期” | ❌ | 存取周期 / 总线周期 |
| ”DMA 期间 CPU 完全停止” | ❌ | 只有”停止 CPU 访存”方式才接近这样;其余两种 CPU 照常执行不访存的操作 |
| ”交替访存需要申请总线” | ❌ | 预先硬连线分时,无握手 |
| ”DMA 数据要先送到 CPU 寄存器” | ❌ | 直接进主存,这是 DMA 的定义性特征 |
| ”中断请求的优先级高于 DMA 请求” | ❌ | 反了,DMA 优先,因为数据会丢 |
| ”DMA 响应比中断响应慢” | ❌ | 更快,等待粒度是总线周期而非指令 |
| ”有了硬件 Cache 一致性就不用管 DMA 了” | ⚠️ | 一致性解决了,但总线争用仍在 |
| ”DMA 方式下 CPU 与外设完全并行” | ⚠️ | 逻辑上并行,但争总线时仍有物理冲突 |
对照速查
| 三种争总线方式 | DMA 占用 | CPU 停顿 | 复杂度 |
|---|---|---|---|
| 停止 CPU 访存 | 整块 | 长 | 低 |
| 周期挪用 | 一个存取周期 | 短 | 中 |
| 交替访存 | 固定半周期 | 无 | 高 |
| 三阶段 | 谁做 |
|---|---|
| 预处理 | CPU |
| 数据传送 | DMA 控制器 |
| 后处理 | CPU(中断服务程序里) |
| INT | HRQ | |
|---|---|---|
| 响应时机 | 指令结束 | 总线周期结束 |
| 保存断点 | 要 | 不要 |
| 优先级 | 低 | 高 |
考点
- 三种争总线方式的对照,尤其”交替访存不需要申请”。
- 周期挪用的三种情形及 DMA 优先的理由。
- DMA 请求与中断请求的五点差异——高频大题的送分点。
- 三阶段的分工:首尾归 CPU。
- DMA 控制器的主/从双重身份。
- 性能计算,注意区分 CPU 占用率与总线占用比。
- Cache 一致性的两个方向及三种解法。
链接
- 🏠 返回总览:计算机组成原理第 7 章:输入/输出系统总览
- ⬅️ 上一节:7.3.2 程序中断方式
- 🔑 主/从设备判据:6.1.1 总线的基本概念
- 🔗 总线带宽公式:6.1.5 总线的性能指标
- 🔗 Cache 一致性:3.5.5 Cache 一致性问题
- 🔗 四种方式对照:7.1.3 I/O 控制方式
- 💻 OS 对应:OS 5.1.2 I/O 控制方式
- 📖 名词库:第 7 章名词库