1. DMA 基础概念
1.1 DMA 工作原理
直接存储器访问(Direct Memory Access, DMA)是一种允许硬件设备直接与系统内存交换数据而无需 CPU 逐字节干预的机制。在嵌入式 Linux 系统中,DMA 是高性能 I/O 和实时数据处理的核心支撑技术。
核心思想:CPU 只需发起传输请求,DMA 控制器接管总线控制权,完成数据搬运后通过中断通知 CPU。CPU 从“搬运工”转变为“管理者”。
DMA 传输的基本流程如下:
- 初始化:CPU 配置 DMA 控制器的源地址、目的地址、传输长度、传输方向等参数。
- 启动:CPU 向 DMA 控制器发送启动命令,DMA 控制器获得总线控制权。
- 传输:DMA 控制器在源与目的之间直接搬运数据,每次传输后递增地址并递减计数器。
- 完成:当计数器归零,DMA 控制器释放总线,向 CPU 发送中断信号。
- 处理:CPU 在中断服务程序中处理传输完成后的数据。
1.2 DMA 与 CPU 的协同
DMA 与 CPU 的协同工作涉及总线仲裁、缓存一致性、中断同步三个关键维度。理解这些协同机制是优化 DMA 中断处理延迟的前提。
| 协同维度 | 描述 | 典型问题 |
|---|---|---|
| 总线仲裁 | DMA 控制器与 CPU 竞争系统总线使用权,由仲裁器决定优先级 | DMA 占用总线过长导致 CPU 饥饿 |
| 缓存一致性 | DMA 直接访问物理内存,CPU 缓存中的数据可能过时 | 脏缓存未刷新导致数据错误 |
| 中断同步 | DMA 传输完成后通过中断通知 CPU,CPU 在中断上下文中处理结果 | 中断处理延迟影响实时性 |
实践要点:在 Linux 内核中,DMA 缓冲区通常使用
dma_alloc_coherent() 分配一致性内存,或使用 dma_map_single() 配合缓存刷新操作来保证一致性。
DMA 与 CPU 的协同模式可分为以下三种:
- 轮询模式:CPU 循环检查 DMA 状态寄存器,适用于短传输或低实时性场景。
- 中断模式:DMA 完成时触发中断,CPU 响应中断处理数据,是嵌入式 Linux 中最常用的模式。
- 描述符链模式:DMA 自动加载下一个描述符,连续执行多个传输,仅在整链完成后触发一次中断。
1.3 DMA 传输模式
根据数据传输的组织方式和触发条件,DMA 传输模式可分为以下四种:
| 传输模式 | 触发方式 | 适用场景 | 中断频率 |
|---|---|---|---|
| 单次传输 | 软件启动 | 小数据块、一次性传输 | 每次传输完成 |
| 循环传输 | 硬件自动重载 | 音频流、连续采样 | 每轮循环完成 |
| 描述符链传输 | 链表自动加载 | 大数据块、分散-聚集 | 整链完成 |
| 请求触发传输 | 外设请求信号 | UART RX/TX、SPI 传输 | 每次请求完成 |
延迟陷阱:单次传输模式在每次传输后都触发中断,高频中断会导致 CPU 上下文切换开销剧增。在实时系统中,应优先使用描述符链模式合并中断,或使用循环模式配合双缓冲机制。
描述符链传输是嵌入式 Linux 中最常用的高性能模式,其工作原理如下:
- CPU 在内存中构建描述符链表,每个描述符包含源地址、目的地址、传输长度、下一个描述符指针。
- DMA 控制器加载第一个描述符并执行传输。
- 传输完成后,DMA 自动从当前描述符中读取下一个描述符地址并加载。
- 当遇到描述符的“链结束”标志时,DMA 停止并触发中断。
性能对比:在 1 MB 数据传输场景下,单次传输模式(每次 1 KB)会产生 1024 次中断;而描述符链模式(每条链 256 KB)仅产生 4 次中断,中断开销降低 99.6%。
选择 DMA 传输模式时,需综合考虑以下因素:
- 数据量:小数据量(< 64 字节)适合单次传输;大数据量适合描述符链。
- 实时性:循环传输配合双缓冲可保证固定延迟;描述符链的延迟取决于链长度。
- 外设特性:请求触发模式适用于不定长数据的外设(如 UART)。
- 中断负载:中断频率直接影响系统实时性,应尽量合并中断。