4. DMA与PIO:硬件如何帮我们搬数据
说到内存拷贝,很多人第一反应就是 memcpy。但你想过没有,CPU 其实并不适合干这种「搬砖」的活。它擅长的是计算、判断、分支预测。你让一个数学家去搬砖,是不是有点浪费?
我早年做嵌入式系统时,就踩过这个坑。一个视频采集卡,CPU 占用率飙到 90%,结果一查,全是 PIO 模式在作祟。后来换成 DMA,CPU 占用直接降到 15%。嗯,这就是我们今天要聊的核心——DMA 和 PIO 的区别。
4.1 PIO:CPU 亲自下场搬数据
PIO,全称 Programmed I/O,编程输入输出。说白了,就是 CPU 亲自下场,一条指令一条指令地把数据从外设搬到内存,或者从内存搬到外设。
举个例子,你要从硬盘读一个扇区(512 字节)到内存:
// PIO 模式下的硬盘读取(伪代码)
for (int i = 0; i < 512; i++) {
// CPU 从硬盘数据端口读一个字节
byte data = inb(0x1F0);
// CPU 把这个字节写入内存缓冲区
buffer[i] = data;
}
你看,每个字节都要 CPU 亲自参与。512 字节还好,如果是 1MB 呢?CPU 就完全被绑死在 I/O 上了,没法干别的。
PIO 的核心问题:CPU 在搬运数据期间,无法执行其他任务。这叫「忙等」(busy waiting)。
我在项目中遇到过一个问题:一个老旧的串口设备,驱动用的是 PIO 模式。每次收发几百字节数据,CPU 就卡死在那。后来我换成 DMA,问题迎刃而解。说白了,PIO 只适合小数据量、低频率的场景。
4.2 DMA:硬件帮你搬,CPU 去喝茶
DMA,全称 Direct Memory Access,直接内存访问。它引入了一个专门的硬件——DMA 控制器(DMAC),专门负责搬数据。
CPU 只需要告诉 DMA 控制器三件事:
- 源地址:数据从哪来
- 目标地址:数据搬到哪去
- 数据长度:搬多少字节
然后 CPU 就可以去干别的事了。DMA 控制器自己完成搬运,搬运完成后发一个中断通知 CPU。
// DMA 模式下的硬盘读取(伪代码)
// 1. 配置 DMA 控制器
dma_set_source(0x1F0); // 硬盘数据端口
dma_set_destination(buffer); // 内存缓冲区
dma_set_length(512); // 512 字节
dma_start(); // 启动 DMA
// 2. CPU 可以去做其他事情
do_other_work();
// 3. 等待 DMA 完成中断
wait_for_dma_interrupt();
个人建议:在 Linux 驱动开发中,尽量使用 DMA API(如 dma_map_single、dma_alloc_coherent),而不是自己写 PIO 循环。性能差距是数量级的。
4.3 DMA vs PIO:一张表看懂
| 对比项 | PIO | DMA |
|---|---|---|
| CPU 参与度 | 全程参与,每个字节都要 CPU 搬 | 仅初始化配置,搬运由硬件完成 |
| CPU 利用率 | 低(CPU 被 I/O 绑死) | 高(CPU 可并行处理其他任务) |
| 吞吐量 | 低(受限于 CPU 指令执行速度) | 高(硬件直接搬运,带宽更大) |
| 延迟 | 低(无额外硬件开销) | 略高(DMA 配置和中断有开销) |
| 适用场景 | 小数据量、低频、简单外设 | 大数据量、高频、高性能外设 |
| 硬件复杂度 | 低(不需要 DMA 控制器) | 高(需要 DMA 控制器支持) |
这里要注意一点:DMA 并不是万能的。对于小数据量(比如几十字节),DMA 的配置开销可能比 PIO 还大。我见过有人用 DMA 传 4 个字节,结果配置时间比传输时间还长。嗯,这就有点得不偿失了。
4.4 DMA 的三种传输模式
DMA 控制器也不是傻搬,它有三种工作模式:
- 单次传输(Single Transfer):每次 DMA 请求只传输一个数据单元(字节/字),然后释放总线。适合慢速设备。
- 块传输(Block Transfer):一次请求传输整个数据块,期间占用总线不放。适合高速设备,但会影响其他设备访问内存。
- 请求传输(Demand Transfer):由外设控制传输节奏。外设准备好数据就发请求,DMA 就搬。适合串口这类不定速设备。
我曾经踩过的坑:在某个 FPGA 项目中,我用了块传输模式,结果 DMA 占着总线不放,导致 CPU 无法及时响应中断,系统直接卡死。后来改成请求传输模式,问题解决。记住:块传输虽快,但别让它饿死 CPU。
4.5 知识体系:DMA 与 PIO 的核心逻辑
下面这张图,是我自己总结的 DMA 与 PIO 的核心逻辑对比。你可以看到,PIO 是 CPU 亲自下场,DMA 是 CPU 派了个小弟去干。
4.6 实际项目中的选择策略
说了这么多,到底什么时候用 PIO,什么时候用 DMA?我个人的经验是:
- 数据量 < 64 字节:用 PIO。DMA 的配置开销(设置寄存器、分配缓冲区、处理中断)可能比传输本身还大。
- 数据量 64 字节 ~ 1KB:看情况。如果 CPU 很闲,PIO 也行;如果 CPU 很忙,果断 DMA。
- 数据量 > 1KB:必须用 DMA。否则 CPU 会被 I/O 拖死。
避坑指南:我曾经在一个网络驱动中,用 PIO 模式处理 1500 字节的以太网帧。结果网络吞吐只有 200Mbps,CPU 占用 100%。换成 DMA 后,吞吐直接飙到 950Mbps,CPU 占用降到 20%。你看,选对模式,性能翻倍。
好了,DMA 和 PIO 的区别就聊到这。记住一句话:CPU 是用来计算的,不是用来搬砖的。把搬砖的活交给 DMA,你的系统吞吐才能上去。