1. DMA 基础概念
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件子系统独立于 CPU 直接读写系统内存的机制。在 DMA 传输过程中,CPU 只需初始化传输任务,随后由 DMA 控制器接管数据搬运工作,传输完成后通过中断通知 CPU。
核心思想:将数据搬运从 CPU 卸载到专用硬件,实现 CPU 与 I/O 操作的并行执行。
1.2 DMA 与 CPU 数据搬运的区别
传统 PIO(Programmed I/O)模式下,CPU 逐字节或逐字搬运数据,完全占用处理器核心。DMA 模式下,CPU 仅参与传输设置和完成处理,数据路径绕过 CPU 核心。
| 对比维度 | CPU 搬运 (PIO) | DMA 搬运 |
|---|---|---|
| 数据路径 | 外设 → CPU 寄存器 → 内存 | 外设 ↔ 内存(直通) |
| CPU 占用率 | 100%(传输期间) | 仅初始化 + 中断处理(<5%) |
| 传输粒度 | 字节/字(软件循环) | 块/描述符链(硬件自动) |
| 吞吐量 | 受限于 CPU 频率 | 受限于总线带宽 |
| 延迟 | 低(无额外硬件开销) | 中等(DMA 引擎启动延迟) |
| 适用场景 | 小数据量、控制寄存器访问 | 大数据块、流式 I/O |
经验法则:单次传输小于 64 字节时,PIO 可能比 DMA 更高效,因为 DMA 引擎的启动开销超过了节省的 CPU 时间。
1.3 DMA 传输的优势与适用场景
核心优势
- CPU 卸载:释放处理器执行应用逻辑,而非数据搬运。
- 高吞吐量:利用总线突发传输,接近硬件理论带宽。
- 低延迟抖动:硬件状态机保证确定性传输时序。
- 支持后台传输:DMA 可与 CPU 缓存/指令流水线并行工作。
- 描述符链:支持 scatter-gather,无需连续物理内存。
典型适用场景
| 场景 | 说明 | 典型带宽需求 |
|---|---|---|
| 网络数据包收发 | 网卡 DMA 直接将帧写入内存 Ring Buffer | 1 Gbps ~ 100 Gbps |
| 存储设备读写 | NVMe/SATA 控制器 DMA 搬运块数据 | 500 MB/s ~ 14 GB/s |
| 多媒体采集 | 摄像头/音频 ADC 通过 DMA 填充帧缓冲区 | 10 MB/s ~ 2 GB/s |
| GPU 纹理传输 | CPU 与 GPU 间通过 DMA 交换顶点/纹理数据 | 10 GB/s+ |
| FPGA 加速器 | 自定义硬件通过 DMA 与主机内存交换数据 | 取决于 PCIe 通道数 |
不适用场景
- 小数据量控制操作:如配置寄存器、读取状态字(通常 < 64 字节)。
- 非连续随机访问:DMA 对非连续地址的预处理开销较大。
- 实时性要求极高的短事务:DMA 引擎启动延迟(通常 1~10 μs)可能不可接受。
- 硬件不支持 DMA 的外设:老旧 UART、GPIO 等。
常见误区:并非所有外设都适合 DMA。如果每次传输前都需要修改传输参数(如动态变长协议头),DMA 的配置开销可能抵消收益。此时应考虑混合模式:小头部用 PIO,大数据载荷用 DMA。
1.4 DMA 传输模式分类
| 模式 | 描述 | 典型应用 |
|---|---|---|
| 单块传输 | 一次传输一块连续数据,完成后中断 | 简单 ADC 采样、单帧图像采集 |
| 循环缓冲 | DMA 在固定缓冲区循环写入,地址自动回绕 | 音频流、连续数据采集 |
| 描述符链 | 多个 DMA 描述符组成链表,硬件自动加载下一个 | 网络数据包、存储队列 |
| Scatter-Gather | 一次 DMA 操作读写多个非连续内存块 | 文件系统 I/O、虚拟内存映射 |
Linux 内核视角:DMA 引擎驱动通过
dmaengine 子系统统一管理,提供 dma_device 抽象层。传输描述符使用 dma_async_tx_descriptor 结构体,支持回调函数和事务提交。