DMA 基础概念:DMA 定义、DMA 控制器、DMA 传输模式
各位好,欢迎来到这门课程的开篇。我是你们这趟技术旅程的向导,一个在系统性能调优坑里爬摸滚打多年的老工程师。咱们第一站,不急着上手配中断,先把基石打牢。聊聊 DMA 到底是个什么东西。
我见过不少新手,甚至一些老手,对 DMA 的理解就停留在“能加速”三个字上。但具体怎么加速的?绕过谁了?加速的代价是什么?很多人一知半解。嗯,这章我们就把这几个问题掰开揉碎了讲清楚。
什么是 DMA?—— 说白了,就是 CPU 的专职快递员
DMA 的全称是 Direct Memory Access,直接存储器访问。你不用死记这个全称,只要记住它的核心作用就行:让数据在设备和内存之间搬家,不占用 CPU 干活。
你想想看,如果没有 DMA,一个网卡收到一个数据包,CPU 必须停下来,亲自把这个包从网卡缓冲区拷贝到系统内存里。一次拷贝还行,如果每秒涌入几万个数据包,CPU 光干这搬运的活就累得半死,根本没时间处理上层协议和业务逻辑。
那有了 DMA 之后呢?CPU 只需要告诉 DMA 控制器:“嘿,帮我把网卡的数据搬 1KB 到内存地址 0x1000 去,搬完了吱一声。”然后 CPU 就可以转身处理别的事情去了。DMA 控制器就像一个极其高效的专职快递员,默默把活干完,最后通过一个中断信号通知 CPU:“货已签收。”
DMA 控制器——这场演出的幕后导演
DMA 控制器,简称 DMAC,是执行搬运工作的核心硬件模块。早期的 DMA 控制器是主板上独立的芯片,比如经典的 Intel 8237。现在基本上都集成在 SoC 内部了。但它的职责没变:接管总线的控制权,执行数据传输。
我重点要跟你聊聊 DMAC 的几个关键“器官”,你了解这些,以后读芯片手册才能抓到重点:
- 地址寄存器:存放数据搬运的源地址和目标地址。它必须能自增或自减,这样才能连续搬运一大块数据。
- 计数寄存器:记录还需要搬运多少字节。每搬运一个单元,计数减一。归零时表示传输完成。
- 控制寄存器:配置传输模式(下文会详述)、传输宽度(字节、半字、字)、是否开启中断等。这里是程序员与硬件对话的窗口。
- 状态寄存器:告诉 CPU 当前传输是否完成,有没有发生错误。
我在一个嵌入式网络项目里就碰到过一个问题:用老款的 DMA 控制器传输大块数据,发现数据错位。排查了半天,发现是 DMA 的地址设置触发了缓存一致性问题。说白了,就是 CPU 缓存里的数据和内存里的数据不一致,DMA 直接往内存里写了脏数据,而 CPU 还傻呵呵地读自己的缓存。这属于经典坑,稍后我会告诉你如何通过配置来规避。
DMA 的三种主要传输模式
不同的场景需要不同的 DMA 工作模式。我把最常用的三种给你列出来,你可以当成一个速查表:
| 模式名称 | 工作方式 | 适用场景 |
|---|---|---|
| 单块传输模式 | DMA 占用总线,一口气把一整块数据从 A 搬到 B,期间不释放总线控制权。 | 高优先级、数据量已知且较小的传输(比如 UART 的单次发送)。 |
| 请求传输模式 | 每次传输一个或多个数据单元后,DMA 释放总线,等待硬件外设再次发出请求信号才继续搬。 | 慢速外设(如硬盘、低速网卡),防止 DMA 长期霸占总线,影响 CPU 响应。 |
| 链式传输模式 | DMAC 中的寄存器可以被硬件自动装载。硬件支持一个描述符链表,DMA 自动处理下一个传输任务,无需 CPU 干预。 | 高速高性能场景(如 NVMe SSD、万兆网卡),需要处理大量分散/聚集的 IO 请求。 |
DMA 的核心知识体系图谱
下面的 SVG 图可以帮助你快速建立本章节的知识脉络:从 DMA 的发起方(CPU 和软件),到执行方(DMA 控制器),再到它的具体工作模式,以及最终如何与中断相连。
DMA 与中断的姻亲关系
为什么要在一个讲 DMA 的章节提中断?因为它们是天生的搭档。DMA 帮 CPU 搬完了数据,必须有个机制通知 CPU“活干完了”。这个机制就是中断。
这里面有个性能关键点:中断的处理延迟和亲和性。DMA 完成中断在哪个 CPU 核上响应,直接决定了数据包的吞吐延迟。如果一个高速网卡的 DMA 中断老是落在缓存紧张的 CPU 核心上,那 CPU 又要去内存里把数据抓回来,之前 DMA 加速带来的红利全白费了。这就是我们后续课程要深入探讨的“中断亲和性”的起点。