1. 课程导论:DMA 与 mmap 在 Linux 驱动中的核心价值
在嵌入式 Linux 系统中,平台驱动 (Platform Driver) 是连接硬件外设与内核框架的标准总线模型。当外设需要搬运大量数据(如摄像头帧、音频流、高速 ADC 采样)时,单纯依赖 CPU 逐字节拷贝会严重拖累系统吞吐量。本课程聚焦两大核心技术——DMA(直接内存访问)与 mmap(内存映射),揭示它们如何在平台驱动中协同工作,实现零拷贝、低延迟的数据传输路径。
核心观点: DMA 解决“谁搬数据”的问题,mmap 解决“用户态如何高效访问数据”的问题。两者结合,可让外设数据直接流入用户空间,CPU 仅做配置与同步。
1.1 DMA 在 Linux 驱动中的核心价值
DMA 控制器独立于 CPU,能在内存与外设之间、或内存与内存之间直接搬运数据。在平台驱动中注册 DMA 通道后,一次传输可达到数百 MB/s 甚至 GB/s 级别,且 CPU 仅在传输开始和完成时介入。
| 对比维度 | PIO(程序控制 I/O) | DMA(直接内存访问) |
|---|---|---|
| 数据搬运主体 | CPU 逐字拷贝 | DMA 控制器硬件搬运 |
| CPU 占用率 | 高(传输期间被占满) | 低(仅启动和中断处理) |
| 吞吐量上限 | 受限于 CPU 主频与缓存 | 受限于总线带宽(通常更高) |
| 适用场景 | 小数据量、控制寄存器访问 | 大数据块、流式数据(音视频、网络) |
实战提示: 在嵌入式平台(如 i.MX、STM32MP1、Zynq)上,DMA 通常由硬件描述(Device Tree)声明,驱动通过
dma_request_chan() 获取通道,使用 dmaengine_prep_slave_sg() 准备传输描述符。
1.2 mmap 在 Linux 驱动中的核心价值
传统用户态读取设备数据需要经过 read() 系统调用 → 内核缓冲区 → 拷贝到用户缓冲区,至少两次拷贝。mmap 将内核空间的一段物理内存或 DMA 缓冲区直接映射到用户进程的虚拟地址空间,用户态程序可以像访问普通数组一样直接操作数据,完全消除内核态与用户态之间的数据拷贝。
| 访问方式 | 数据拷贝次数 | 上下文切换 | 延迟 |
|---|---|---|---|
read() / write() |
至少 2 次(内核↔用户) | 每次调用 1 次系统调用 | 高(μs 级) |
mmap() |
0 次(直接映射) | 仅映射时 1 次系统调用 | 低(ns 级访问) |
注意事项: mmap 映射的物理内存必须连续(或通过
remap_pfn_range() 处理),且需要处理缓存一致性(Cache Coherency)。对于 DMA 缓冲区,通常使用 dma_alloc_coherent() 分配一致性内存,或使用 dma_map_single() 配合 DMA_FROM_DEVICE 方向进行同步。
1.3 DMA 与 mmap 协同:平台驱动的终极数据通路
在平台驱动中将 DMA 与 mmap 结合,形成一条完整的硬件→用户空间零拷贝链路:
- DMA 传输阶段: 外设数据通过 DMA 控制器直接写入预先分配的物理内存缓冲区(通常使用
dma_alloc_coherent()分配)。 - mmap 映射阶段: 驱动实现
file_operations中的.mmap回调,调用dma_mmap_coherent()将同一块物理内存映射到用户进程的虚拟地址空间。 - 用户态访问: 应用程序直接读取映射后的虚拟地址,获取最新 DMA 数据,无需任何拷贝。
典型应用场景: 工业相机采集、软件定义无线电(SDR)、高速数据采集卡、AI 推理预处理管线。在这些场景中,数据吞吐量常超过 500 MB/s,传统 read/write 方式完全无法满足实时性要求。
1.4 课程目标与前置知识要求
通过本课程,你将掌握以下核心能力:
- 平台驱动框架: 理解
platform_driver与platform_device的匹配机制,以及 Device Tree 中 DMA 与内存资源的描述方法。 - DMA 引擎 API: 熟练使用 Linux DMA Engine 子系统(
dma_request_chan、dmaengine_prep_slave_sg、dmaengine_submit、异步回调)。 - mmap 实现: 在驱动中正确实现
.mmap回调,使用remap_pfn_range或dma_mmap_coherent映射物理内存。 - 协同实战: 编写一个完整的平台驱动,实现外设 DMA 传输数据到内核缓冲区,并通过 mmap 暴露给用户态程序直接读取。
| 前置知识领域 | 具体要求 | 推荐参考 |
|---|---|---|
| C 语言 / 内核编程 | 熟悉指针、内存管理、内核模块编写 | 《Linux Device Drivers》第 1-2 章 |
| Linux 驱动基础 | 理解 file_operations、字符设备、platform 总线 | 本系列前序课程《platform 驱动入门》 |
| 内存管理概念 | 了解虚拟地址、物理地址、页表、MMU | 《深入理解 Linux 内核》第 8 章 |
| 硬件基础 | 了解 DMA 控制器基本工作原理、中断机制 | 芯片参考手册(如 i.MX8M TRM) |
学习建议: 本课程包含大量可编译运行的示例代码。建议准备一块支持 DMA 的嵌入式开发板(如 i.MX8M Mini、STM32MP157、Raspberry Pi 4 + 外部 DMA 芯片),或使用 QEMU 的 virtio 平台进行模拟验证。