2. DMA 基础概念:DMA 工作原理、总线主控与从控、DMA 传输方向
DMA(Direct Memory Access,直接存储器访问)是 PCIe 设备实现高性能数据传输的核心机制。在 PCIe 驱动开发中,理解 DMA 的工作原理、总线主控/从控角色以及传输方向,是正确配置地址映射和处理中断的前提。本节将系统梳理这些基础概念,并通过 SVG 图例和表格进行对比说明。
2.1 DMA 工作原理
DMA 允许外设(如网卡、存储控制器、GPU)绕过 CPU,直接在系统内存与设备之间传输数据。其核心思想是:CPU 只需发起传输任务,而实际的搬运工作由 DMA 控制器或支持总线主控的 PCIe 设备完成,从而释放 CPU 去处理其他计算任务。
关键优势: 降低 CPU 占用率,提升系统吞吐量,尤其适合大数据块传输(如网络包、磁盘 I/O)。
典型的 DMA 传输流程如下:
- CPU 准备缓冲区: 驱动程序在系统内存中分配 DMA 缓冲区,并将物理地址(或 IOMMU 转换后的地址)写入设备寄存器。
- CPU 发起传输: 驱动程序通过写设备控制寄存器(如门铃寄存器)通知设备开始 DMA 操作。
- 设备执行传输: PCIe 设备作为总线主控,通过 PCIe 总线直接读写系统内存,无需 CPU 逐字节搬运。
- 传输完成通知: 设备通过 MSI/MSI-X 中断或传统 INTx 中断通知 CPU 传输完成。
- CPU 处理数据: 中断服务例程(ISR)检查传输状态,处理已完成的数据缓冲区。
注意: 在支持 IOMMU(如 Intel VT-d、AMD-Vi)的系统中,设备看到的地址是 IOVA(I/O Virtual Address),而非物理地址。驱动必须使用 DMA API(如
dma_map_single)进行地址转换。
2.2 总线主控与总线从控
在 PCIe 体系中,设备根据其能否主动发起总线事务,分为两种角色:
| 角色 | 定义 | 典型设备 | DMA 能力 |
|---|---|---|---|
| 总线主控(Bus Master) | 能够主动发起 PCIe 存储器读写、I/O 读写或配置读写事务的设备。 | 网卡、NVMe SSD、GPU、RAID 控制器 | 支持 DMA,可自主访问系统内存 |
| 总线从控(Bus Slave / Target) | 只能被动响应总线主控发起的事务,不能主动发起事务。 | 传统 UART、GPIO 控制器、低速传感器 | 不支持 DMA,依赖 CPU 或 DMA 控制器搬运数据 |
驱动开发要点: 在 PCIe 驱动中,要使能设备的总线主控能力,必须在初始化时设置 PCI 命令寄存器中的“Bus Master Enable”位(bit 2)。否则,即使设备硬件支持 DMA,也无法发起任何存储器事务。
总线主控与从控的交互关系如下图所示:
2.3 DMA 传输方向
DMA 传输方向决定了数据在设备与系统内存之间的流动路径。在驱动开发中,正确设置传输方向是保证数据一致性和避免缓存问题的关键。
| 传输方向 | 描述 | 典型场景 | 驱动 API 方向标志 |
|---|---|---|---|
| 设备到内存(DMA 读) | 设备从内部缓冲区读取数据,写入系统内存。 | 网卡接收数据包、磁盘读取数据 | DMA_FROM_DEVICE |
| 内存到设备(DMA 写) | 设备从系统内存读取数据,写入设备内部缓冲区。 | 网卡发送数据包、磁盘写入数据 | DMA_TO_DEVICE |
| 双向传输(Bidirectional) | 设备与内存之间可双向传输,通常用于控制/状态同步。 | GPU 纹理上传/下载、FPGA 数据交换 | DMA_BIDIRECTIONAL |
重要提示: 方向标志影响 DMA API 的缓存一致性操作。例如,
dma_map_single(dev, cpu_addr, size, DMA_FROM_DEVICE) 会在传输前刷新 CPU 缓存,确保设备写入的数据不会被 CPU 缓存污染。使用错误的方向标志可能导致数据损坏。
2.4 总结与对比
下表汇总了 DMA 基础概念的核心要点,便于快速回顾:
| 概念 | 核心要点 | 驱动关注点 |
|---|---|---|
| DMA 工作原理 | 设备直接访问内存,CPU 仅负责发起和完成处理 | 使用 DMA API 进行地址映射和同步 |
| 总线主控 | 设备主动发起 PCIe 事务,是 DMA 的前提 | 必须设置 PCI 命令寄存器的 Bus Master Enable 位 |
| 总线从控 | 设备只能被动响应,不能主动发起事务 | 不支持 DMA,通常用于控制/状态寄存器访问 |
| DMA 传输方向 | 决定数据流方向,影响缓存一致性策略 | 使用正确的 DMA_FROM_DEVICE / DMA_TO_DEVICE / DMA_BIDIRECTIONAL |