1. DMA 基础概念

1.1 DMA 工作原理

直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存交换数据,而无需 CPU 逐字节干预的机制。在嵌入式 Linux 系统中,DMA 是高性能外设(如网卡、存储控制器、GPU、多媒体编解码器)实现高吞吐量数据传输的核心基础。

DMA 传输的基本流程如下:

  1. CPU 配置 DMA 控制器:设置源地址、目的地址、传输长度、传输方向(内存→设备、设备→内存、内存→内存)以及触发条件。
  2. 外设请求 DMA 传输:当外设准备好数据或需要数据时,向 DMA 控制器发送硬件请求信号(DMA Request)。
  3. DMA 控制器接管总线:DMA 控制器获得总线控制权,直接在外设与内存之间搬运数据,CPU 可并行执行其他任务。
  4. 传输完成中断:DMA 传输完成后,DMA 控制器向 CPU 发送中断信号,通知驱动程序处理数据。
关键理解:DMA 的本质是“CPU 委托数据搬运任务给专用硬件”,从而将 CPU 从逐字节拷贝中解放出来,专注于控制逻辑和计算密集型任务。
DMA 工作原理流程图 DMA 工作原理流程图 CPU 配置 DMA 控制器 DMA 控制器 搬运数据 外设 请求 DMA 系统内存 数据缓冲区 配置 请求 数据传输 完成中断 中断 CPU 配置后即可并行执行其他任务,DMA 传输完成后通过中断通知 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 进行配置。
DMA 传输模式对比 DMA 传输模式对比 块传输 一次性传输完整数据块 持续占用总线 高吞吐量 周期窃取 每个周期窃取一个时钟 对 CPU 干扰小 传输速率较低 透明传输 仅在 CPU 空闲时传输 完全不干扰 CPU 实现复杂 模式选择原则 块传输:适合大数据量、高优先级传输(如显示刷新) 周期窃取:适合实时性要求高、数据量适中的场景(如音频) 透明传输:适合后台低优先级数据搬运(如日志记录)

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 需要解决的核心问题之一,后续章节将深入探讨。
DMA 在嵌入式系统中的作用架构 DMA 在嵌入式系统中的作用架构 CPU 控制与计算 DMA 控制器 数据搬运 配置 网络 以太网/WiFi 存储 eMMC/UFS/NVMe 多媒体 Camera/Display 音频/采集 I2S/ADC/SPI DMA 将 CPU 从数据搬运中解放,使嵌入式系统能够同时处理网络、存储、多媒体等高带宽任务 但 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 一致性,驱动无需额外操作 性能最佳,但需确认硬件是否真正支持