核心判断
DMA 在真实驱动里解决的,从来不只是“少拷几次”。它真正解决的是:让数据搬运脱离 CPU 主执行路径,把吞吐、时延和 CPU 占用从同一个瓶颈里拆出来。
这一章解决什么问题
很多人学 DMA,一上来就背定义:Direct Memory Access,设备可直接访问内存。定义当然没错,但工程价值接近零。
你真正该问的是:
- 为什么有些驱动用 PIO 还能活,一上吞吐就开始抽风?
- 为什么一旦进入音频、网络、视频、采集这类持续流场景,DMA 很快从“可选优化”变成“通路基础设施”?
- 为什么同样是“能跑”,有的实现 CPU 忙得像狗,有的实现却能把 CPU 留给真正的控制逻辑?
先把错误认知打掉
误区 1:DMA 只是性能优化
错。对很多驱动来说,DMA 不是锦上添花,而是决定架构形态的分水岭。
没有 DMA,你的数据路径通常是:
- 设备发出事件
- CPU 进入中断或轮询
- CPU 一次次从寄存器/FIFO 搬数据
- 数据量一大,CPU 时间被搬运吞掉
有 DMA 后,路径变成:
- 驱动配置 buffer 和描述符
- 设备直接把数据写进内存,或从内存取数据
- CPU 主要处理提交、完成、回收和控制逻辑
区别不在“是否更快”,而在“CPU 是否还亲自扛着数据搬运这口锅”。
误区 2:只要吞吐高就一定上 DMA
也错。DMA 不是白送的。
你引入 DMA 的同时,也引入了:
- buffer 生命周期管理
- cache 一致性问题
- ownership 切换
- completion / timeout / reset 清理
- 用户态协议设计(如果走零拷贝)
所以 DMA 的判断标准不是“能不能用”,而是:
引入 DMA 后,整体系统收益是否大于复杂度账单。
CPU 搬运 vs DMA 搬运:本质差别
维度 | CPU 直接搬运 | DMA 搬运 |
数据搬运主力 | CPU | 设备 / DMA 控制器 |
CPU 角色 | 既控制又搬运 | 主要负责配置、同步、完成处理 |
适合场景 | 低吞吐、偶发、控制面操作 | 大块、持续流、低 CPU 占用要求 |
主要瓶颈 | CPU 时间、总线访问频率 | buffer 设计、一致性、调度与回收 |
复杂度来源 | 实现简单,但吞吐差 | 实现复杂,但扩展性强 |
三类最典型场景
1. 一次性大块搬运
比如:固件下载、图像帧一次性传输、大块存储读写。
这类场景的重点是:
- 数据量大
- 一次事务较长
- 对单次搬运效率敏感
DMA 在这里主要解决:别让 CPU 用 memcpy 的姿势给自己上刑。
2. 持续流式收发
比如:音频采集、摄像头视频流、网络包收发、ADC 连续采样。
这类场景里,DMA 往往不是“更快一点”,而是“没有它很难长期稳定运行”。
因为系统要持续面对:
- 周期性数据到达
- ring buffer 推进
- completion 回收
- 上层消费速度不稳定
这也是本专题选择采集型 / 流式设备驱动作为主线案例的原因:它能把 DMA 真正难的部分全暴露出来。
3. 零拷贝用户态协作
比如:用户态直接消费设备采集到的大块数据。
这时 DMA 不只是解决“搬运”,还会反过来要求你设计:
- buffer ownership
mmap数据面
ioctl/poll控制面
- submit / wait / consume 协议
从这里开始,DMA 已经不是函数调用问题,而是系统协作协议问题。
哪些设备天然更需要 DMA
设备类型 | 是否应优先考虑 DMA | 原因 |
UART(低速控制) | 视吞吐而定 | 低速下 PIO 足够;高波特率持续流时 DMA 更稳 |
音频接口 I2S / SAI | 通常是 | 持续流、周期稳定、CPU 不应亲自搬样本 |
摄像头 / 视频采集 | 基本是 | 数据量大、帧流持续、缓冲管理复杂 |
网络设备 | 基本是 | 吞吐与延迟要求高,描述符 + ring 是常态 |
ADC / 采集卡 | 通常是 | 连续采样天然适合 DMA 周期搬运 |
I2C 配置型外设 | 通常不是重点 | 控制面为主,搬运量通常不大 |
什么时候不该急着上 DMA
1. 数据量小,而且是低频控制操作
如果只是偶发寄存器读写、小块配置、低速命令通道,DMA 很可能是在给自己加戏。
2. 硬件路径本身不支持,或支持得很残
有些设备“理论支持 DMA”,实际 descriptor、对齐、地址范围、completion 行为都很别扭。这个时候硬上 DMA,得到的通常不是优雅,而是漫长的调试岁月。
3. 你的系统瓶颈根本不在搬运
如果真正卡的是:
- 用户态处理太慢
- 上层协议太重
- 锁竞争严重
- 中断风暴
那你把 PIO 换成 DMA,也只是换一种姿势堵车。
一个工程上真正有用的判断框架
问题 1:数据搬运是否正在占用 CPU 的主路径预算?
如果 CPU 大量时间浪费在搬数据,而不是做控制、调度、协议、算法,那 DMA 值得考虑。
问题 2:数据是否是持续流,而不是偶发点状事件?
越接近持续流,DMA 的收益越大。
问题 3:是否需要把用户态、驱动、设备组成一条高吞吐数据管线?
如果是,那 DMA 往往会直接决定后续 buffer 和协议设计。
问题 4:你是否愿意承担 DMA 带来的治理成本?
包括:
- 生命周期
- ownership
- sync
- reset / timeout 清理
- debug 与验证
如果不愿意,那就别碰。别一边想要高速,一边又只想要玩具级复杂度。工程世界没这种便宜。
一个主线案例的直觉图
这个图里最重要的不是箭头,而是一个残酷事实:
你一旦采用 DMA,就必须开始治理“谁在什么时候拥有 buffer”。
所以本专题后面会把重点放在:
- ownership
- lifecycle
- ring buffer
- mmap 协议
- error path
因为这些才是“真实驱动开发”真正会把人绊倒的地方。
本章结论
- DMA 的本质价值,是把数据搬运从 CPU 主路径中剥离出来。
- 它在一次性大块传输里是加速器,在持续流设备里更像基础设施。
- DMA 不是单纯 API 技巧,而是会反向决定 buffer、协议和调试模型的系统设计。
- 只要进入流式、高吞吐、低 CPU 占用场景,你迟早要面对 DMA,而且最好别用侥幸心理面对。
下一章要干什么
下一章不讲 API,先把一次 DMA 传输的全链路图摊开。
因为在真正写驱动前,你得先知道:
- 数据究竟怎么流
- 控制究竟怎么走
- 设备、内存、cache、用户态各自站在哪一层
否则你后面学的每个函数,都只是在记碎片。





