1. 课程导论:DMA与多核同步的江湖地位

各位同学,欢迎来到这门实战课。

我先问大家一个问题:你写嵌入式代码时,有没有遇到过这种情况——CPU 占用率飙到 90% 以上,但实际有效计算没多少?或者两个核跑着跑着,数据突然就乱了?

嗯,我当年刚入行时,这两个坑都踩过。而且踩得挺深。

1.1 DMA 传输:被低估的“数据搬运工”

DMA,Direct Memory Access,直接存储器访问。说白了,就是让硬件自己搬数据,别老麻烦 CPU。

我见过太多项目,CPU 忙得团团转,结果一分析性能,发现 60% 的时间都在做 memcpy。你说冤不冤?

核心观点: DMA 不是可选项,而是高性能嵌入式系统的必需品。

举个例子。一个 4K 视频帧从摄像头传到 DDR,如果让 CPU 逐字节搬运,大概要消耗 3ms。但用 DMA,只需要配置一次描述符,然后 CPU 就可以去干别的事了。3ms 对 60fps 的流来说,就是 18% 的帧周期——你想想看,这差距有多大。

场景 CPU 搬运 DMA 搬运 性能提升
4KB 数据块 ~12μs ~2μs 6x
1MB 图像帧 ~3ms ~0.4ms 7.5x
持续 100MB/s 流 CPU 锁死 CPU 空闲 质变

我的经验: 在项目中,我习惯把 DMA 描述符链设计成环形缓冲区。这样数据流可以一直跑,不用每次中断都重新配置。曾经有个音频项目,用这个方案把中断频率从 48kHz 降到了 1kHz,CPU 负载直接砍半。

1.2 多核同步:别让“并行”变成“并乱”

多核系统,听起来很美。但实际调试起来,有时候真想摔键盘。

为什么会这样?因为两个核共享同一片内存,你写我读,不加保护的话,数据竞争就像定时炸弹。

我遇到过最惨的一次:一个四核 DSP 项目,跑着跑着,其中一个核突然读到半新不旧的数据,直接算出一个错误控制量,电机当场飞车。嗯,从那以后,我再也不敢轻视同步问题了。

避坑指南: 我曾经以为 spinlock 是万能的。后来发现,在实时系统中,spinlock 持有时间超过 100μs,整个系统的确定性就崩了。锁的粒度,一定要细。

多核同步的核心矛盾就三个:

  • 原子性: 一个操作要么全做,要么没做。不能做一半被另一个核打断。
  • 可见性: 一个核改了数据,另一个核什么时候能看到?缓存一致性协议不是瞬间生效的。
  • 有序性: 编译器重排、CPU 乱序执行,都会让代码执行顺序跟你写的不一样。

这三个问题,任何一个没处理好,系统就会出诡异 bug。而且这种 bug 极难复现——你加个打印,它就好了;你去掉打印,它又来了。

1.3 性能瓶颈的根源分析

咱们把 DMA 和多核同步放在一起看,性能瓶颈的根源其实就三个字:争用。

争用什么呢?

  1. 总线带宽争用: DMA 和 CPU 都抢系统总线。DMA 占得多了,CPU 取指令就慢了。
  2. 缓存一致性开销: 多核共享数据时,缓存一致性协议(比如 MESI)要来回发 snoop 消息。核越多,开销越大。
  3. 锁竞争: 多个核抢同一把锁,没抢到的只能空转。空转就是浪费。

我画了一张图,帮你理清这些关系:

嵌入式系统性能瓶颈根源分析 CPU 核心 DMA 控制器 L1/L2 缓存 DMA 描述符链 系统总线(AXI/AHB) 共享内存(DDR/SRAM) ① 总线争用 ② 缓存一致性开销 ③ 锁竞争

你看,这三个瓶颈是相互关联的。DMA 用多了,总线压力大,CPU 取指令就慢;CPU 慢了,锁持有时间变长,其他核等得更久。一环扣一环。

1.4 课程目标与学习路径

这门课的目标很明确:让你能设计出高性能、确定性的嵌入式系统。

具体来说,学完这门课,你应该能做到:

  • 独立设计 DMA 传输架构,包括描述符链、环形缓冲区和 scatter-gather 模式。
  • 掌握多核同步的核心机制:原子操作、自旋锁、信号量、RCU、无锁队列。
  • 能分析并解决 DMA 与 CPU 之间的缓存一致性问题。
  • 会用 perf、trace 等工具定位性能瓶颈,并给出优化方案。

学习路径我建议这样走:

阶段 内容 实践目标
基础篇(1-10章) DMA 原理、描述符链、多核同步原语 能写出正确的 DMA 驱动和同步代码
进阶篇(11-20章) 缓存一致性、无锁数据结构、性能分析工具 能定位并解决实际性能问题
实战篇(21-30章) 音频流、视频处理、多核调度系统案例 能独立完成一个高性能嵌入式子系统

我的建议: 每章后面的代码示例,一定要自己动手跑一遍。光看是学不会的。我当年学 DMA,就是对着手册一行一行写描述符,写错了就死机,死机了就查寄存器。折腾一个月,才真正搞明白。