1. DMA 基础概念
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存交换数据,而无需 CPU 逐字节干预的机制。在嵌入式 Linux 系统中,DMA 是高性能外设(如网卡、存储控制器、GPU、多媒体编解码器)实现高吞吐量数据传输的核心基础。
DMA 传输的基本流程如下:
- CPU 配置 DMA 控制器:设置源地址、目的地址、传输长度、传输方向(内存→设备、设备→内存、内存→内存)以及触发条件。
- 外设请求 DMA 传输:当外设准备好数据或需要数据时,向 DMA 控制器发送硬件请求信号(DMA Request)。
- DMA 控制器接管总线:DMA 控制器获得总线控制权,直接在外设与内存之间搬运数据,CPU 可并行执行其他任务。
- 传输完成中断:DMA 传输完成后,DMA 控制器向 CPU 发送中断信号,通知驱动程序处理数据。
关键理解:DMA 的本质是“CPU 委托数据搬运任务给专用硬件”,从而将 CPU 从逐字节拷贝中解放出来,专注于控制逻辑和计算密集型任务。
1.2 DMA 传输模式
根据总线控制方式和传输粒度,DMA 主要分为以下三种传输模式:
| 传输模式 | 描述 | 典型应用场景 | 优缺点 |
|---|---|---|---|
| 块传输(Block Transfer) | DMA 控制器一次性传输一个完整的数据块,传输期间持续占用总线 | 高速存储设备(eMMC、NVMe)、帧缓冲更新 | 吞吐量高,但总线占用时间长,可能影响其他设备 |
| 周期窃取(Cycle Stealing) | DMA 控制器在每个总线周期中“窃取”一个时钟周期进行传输,然后释放总线 | 音频流、低速传感器数据采集 | 对 CPU 干扰小,但传输速率较低 |
| 透明传输(Transparent Transfer) | DMA 控制器仅在 CPU 不使用总线时(如 CPU 访问 Cache 时)进行传输 | 多核系统中的后台数据搬运 | 完全不干扰 CPU,但实现复杂,硬件支持有限 |
驱动开发提示:在 Linux DMA 引擎框架中,
dma_transfer_direction 枚举定义了传输方向(DMA_MEM_TO_DEV、DMA_DEV_TO_MEM、DMA_MEM_TO_MEM),而传输模式通常由 DMA 控制器硬件能力决定,驱动通过 dmaengine_slave_config 进行配置。
1.3 DMA 在嵌入式系统中的作用
在嵌入式 Linux 系统中,DMA 的作用贯穿于多个关键子系统,是系统性能的重要保障。以下表格总结了 DMA 在嵌入式系统中的主要应用场景及其作用:
| 子系统 | DMA 作用 | 性能提升效果 | 典型驱动示例 |
|---|---|---|---|
| 网络子系统 | 网卡通过 DMA 直接将网络数据包送入内存缓冲区,CPU 仅处理协议栈 | 千兆以太网线速转发,CPU 占用率 < 10% | drivers/net/ethernet/stmicro/stmmac/ |
| 存储子系统 | eMMC/UFS/NVMe 控制器通过 DMA 搬运块数据,CPU 仅处理文件系统元数据 | 顺序读写速度提升 10~50 倍 | drivers/mmc/host/ |
| 多媒体子系统 | 摄像头、显示控制器、编解码器通过 DMA 传输帧数据 | 4K 视频 60fps 实时处理 | drivers/media/platform/ |
| 音频子系统 | I2S 控制器通过 DMA 搬运音频样本,避免音频卡顿 | 低延迟音频播放(< 10ms) | sound/soc/ |
| 数据采集子系统 | ADC、SPI、UART 通过 DMA 连续采集数据,CPU 仅处理数据 | 高采样率(MHz 级别)连续采集 | drivers/iio/ |
重要提醒:在嵌入式系统中,DMA 虽然能大幅提升性能,但也引入了新的问题——缓存一致性和地址映射。CPU 通过 Cache 访问内存,而 DMA 直接访问物理内存,两者之间可能出现数据不一致。这正是 IOMMU/SMMU 需要解决的核心问题之一,后续章节将深入探讨。
1.4 DMA 与缓存一致性问题
在嵌入式系统中,CPU 通常使用 Cache 来加速内存访问,而 DMA 直接访问物理内存。这种架构差异会导致缓存一致性问题:
- CPU 写入数据到内存:数据可能只停留在 Cache 中,尚未写回物理内存。此时 DMA 从物理内存读取,会读到旧数据。
- DMA 写入数据到内存:数据直接写入物理内存,但 CPU 的 Cache 中可能还保留着旧数据的副本,CPU 读取 Cache 时会读到旧数据。
解决方案:在 Linux 驱动中,通过
dma_map_single() / dma_unmap_single() 等 API 来管理缓存一致性。这些 API 会根据硬件是否支持 IOMMU/SMMU 以及 Cache Coherent Interconnect 来自动执行 Cache 刷新或无效化操作。
下表总结了不同硬件架构下的缓存一致性处理方式:
| 硬件架构 | 缓存一致性处理方式 | 驱动开发注意事项 |
|---|---|---|
| 无 IOMMU,无硬件一致性 | 驱动手动调用 dma_cache_sync() 或使用一致性 DMA 缓冲区(dma_alloc_coherent) |
性能开销大,需谨慎选择缓冲区类型 |
| 有 IOMMU/SMMU,无硬件一致性 | IOMMU 提供地址映射,但缓存一致性仍需驱动或软件处理 | IOMMU 映射后仍需注意 Cache 刷新时机 |
| 有硬件一致性(如 ARM ACE/CHI) | 硬件自动维护 Cache 一致性,驱动无需额外操作 | 性能最佳,但需确认硬件是否真正支持 |