1. DMA 基础概念:工作原理、CPU 交互与 Linux 内核角色
1.1 什么是 DMA?
直接内存访问(Direct Memory Access,DMA) 是一种允许硬件设备(如网卡、磁盘控制器、GPU)直接读写系统内存,而无需 CPU 介入每个数据传输周期的技术。在高速 I/O 场景下,DMA 是降低 CPU 负载、提升系统吞吐量的核心机制。
核心收益: 一次 DMA 传输仅需 CPU 在启动时和完成时参与,中间的数据搬运完全由 DMA 控制器(DMAC)硬件完成。相比 PIO(Programmed I/O)模式,CPU 利用率可提升 10 倍以上。
1.2 DMA 工作原理
DMA 传输的基本流程包含三个阶段:请求、搬运、完成通知。下图展示了典型的 DMA 工作流程:
1.3 DMA 与 CPU 的交互模式
DMA 与 CPU 的交互主要分为三种模式,实际系统中常根据设备特性选择:
| 交互模式 | 描述 | CPU 负载 | 典型场景 |
|---|---|---|---|
| 轮询模式 | CPU 循环检查 DMA 状态寄存器 | 高(持续占用) | 低延迟、短传输(如 SPI) |
| 中断驱动模式 | DMA 完成时触发中断,CPU 响应处理 | 中(仅完成时) | 块设备、网络包收发 |
| 描述符链模式 | DMA 自动加载下一个描述符,批量传输 | 低(批量完成中断) | 高速网卡、NVMe SSD |
实时系统建议: 中断驱动模式是 Linux 内核默认的 DMA 交互方式。在低延迟场景下,需配合中断亲和性(IRQ Affinity)将 DMA 中断绑定到指定 CPU 核心,避免中断在多个核心间迁移导致缓存抖动。
1.4 DMA 在 Linux 内核中的角色
Linux 内核通过 通用 DMA API 为驱动开发者提供统一的 DMA 操作接口,屏蔽了不同架构(x86、ARM、RISC-V)的硬件差异。核心角色包括:
- DMA 映射(Mapping): 将内核虚拟地址转换为设备可访问的物理地址或 IOMMU 地址。
- 一致性内存(Coherent Memory): 为 DMA 分配 CPU 和设备都能直接访问的共享内存区域。
- 流式映射(Streaming Mapping): 针对一次性的 DMA 传输,动态建立和销毁映射关系。
- 中断处理(Interrupt Handling): 将 DMA 完成中断路由到指定 CPU,配合 NAPI 实现高效收包。
下图展示了 Linux 内核中 DMA 子系统的软件架构层次:
1.5 关键数据结构
Linux 内核使用 struct device 中的 dma_ops 指针来管理 DMA 操作。以下是核心数据结构关系:
// include/linux/dma-mapping.h (简化示意)
struct dma_map_ops {
void* (*alloc)(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag,
unsigned long attrs);
void (*free)(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle,
unsigned long attrs);
dma_addr_t (*map_page)(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
unsigned long attrs);
void (*unmap_page)(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction dir,
unsigned long attrs);
int (*dma_supported)(struct device *dev, u64 mask);
};
// 每个设备通过 dev->dma_ops 获取对应架构的实现
struct device {
...
const struct dma_map_ops *dma_ops;
u64 *dma_mask; // 设备 DMA 地址范围
...
};
常见误区: 初学者常误以为 DMA 传输不需要 CPU 参与。实际上,DMA 的启动配置和完成处理仍然需要 CPU 执行指令。在实时系统中,DMA 中断的响应延迟直接影响整体确定性,这也是本课程后续章节重点优化的方向。
1.6 本章小结
- DMA 通过硬件控制器直接搬运数据,释放 CPU 用于计算任务。
- Linux 内核通过通用 DMA API 实现跨架构兼容,驱动开发者无需关心底层细节。
- 中断驱动模式是主流交互方式,中断亲和性设置是实时优化的起点。
- 理解
dma_ops和struct device的关系是深入内核 DMA 子系统的关键。