1. 多核同步基础:从架构到开销

各位同学,咱们今天聊聊多核同步。说实话,这个主题我讲了快十年,每次备课还是会想起当年第一个多核项目翻车的情景。嗯,那会儿我刚从单核转到多核,以为不就是多个核一起跑嘛,结果被同步问题折腾得够呛。

多核同步,说白了就是让多个处理器核心在共享数据时别打架。你想想看,两个核同时写一个变量,谁先谁后?数据会不会乱?这些问题不解决,系统跑着跑着就出妖蛾子了。

多核架构概述

先看看咱们常见的多核架构。嵌入式领域里,主流的有这么几种:

架构类型 典型代表 共享资源 我见过的应用场景
对称多处理(SMP) Cortex-A 系列 内存、外设全共享 高端网关、工业控制
非对称多处理(AMP) Cortex-R + Cortex-M 部分共享,各有私有资源 汽车域控、基带芯片
混合架构(BMP) RISC-V 多核 按需配置共享粒度 AI 边缘计算

我个人习惯把多核系统想象成一个办公室。SMP 就像大家共用一张大桌子,谁都能拿文件,但容易撞手。AMP 呢,每人有自己的小桌子,偶尔去公共柜子取东西。你猜哪种更容易出同步问题?

关键认知:多核架构的选择直接影响同步策略。SMP 下所有核平等,同步机制必须通用;AMP 下可以针对每个核定制,但核间通信更复杂。

我在项目中遇到过最头疼的情况是 AMP 架构下两个核共享一个 DMA 缓冲区。一个核往里写数据,另一个核往外读,没有同步机制的时候,读到的数据经常是半新半旧的——这就是典型的"竞态条件"。

同步机制简介

好,那怎么解决打架问题?嵌入式领域最常用的三把锁:自旋锁、互斥锁、读写锁。咱们一个一个说。

自旋锁(Spinlock)

自旋锁的特点就一个字——等。拿不到锁的线程就在原地打转,不断轮询锁状态。适合锁持有时间极短的场景。

// 自旋锁典型用法(伪代码)
spinlock_t my_lock;
spin_lock(&my_lock);
// 临界区:只有几行代码,执行时间 < 10us
shared_counter++;
spin_unlock(&my_lock);

我曾经踩过的坑:在中断上下文里用了自旋锁,而且忘了关本地中断。结果中断处理函数和普通任务死锁,整个系统卡死。记住:在中断里用自旋锁,一定要用 spin_lock_irqsave() 版本。

互斥锁(Mutex)

互斥锁比自旋锁"温柔"一些。拿不到锁的线程会睡眠,把 CPU 让给别人。适合锁持有时间较长的场景。

// 互斥锁典型用法
mutex_t my_mutex;
mutex_lock(&my_mutex);
// 临界区:可能涉及 I/O 操作,耗时几毫秒
process_data_block();
mutex_unlock(&my_mutex);

你想想看,如果临界区要执行 100ms,用自旋锁的话,其他核就白白空转 100ms,功耗全浪费了。这时候互斥锁就划算得多。

读写锁(RWLock)

读写锁是个聪明的设计。它区分"读"和"写":多个读者可以同时进入,但写者必须独占。适合读多写少的场景。

锁类型 读者间 写者间 读者与写者
自旋锁 互斥 互斥 互斥
互斥锁 互斥 互斥 互斥
读写锁 共享 互斥 互斥

我的经验:读写锁在参数配置表、路由表这类场景特别好用。系统运行时 99% 是读操作,只有配置变更时才写。用读写锁后,读性能几乎不损失。

多核同步的性能开销分析

锁不是免费的。每次加锁解锁都有开销,而且这个开销比你想象的大得多。

我习惯把开销分成三类:

  1. 直接开销:执行加锁/解锁指令的时间。自旋锁最轻,大概几十个 CPU 周期。
  2. 竞争开销:多个核抢锁时,失败的核要么空转(自旋锁),要么被调度出去(互斥锁)。
  3. 伪共享开销:这个最隐蔽。两个核操作不同的变量,但变量恰好在同一个缓存行里,导致缓存一致性协议频繁同步。

咱们用数据说话。我在 Zynq UltraScale+ 上做过实测:

同步方式 无竞争延迟 2 核竞争延迟 4 核竞争延迟 功耗增加
无锁(原子操作) ~10 ns ~15 ns ~20 ns +2%
自旋锁 ~30 ns ~200 ns ~800 ns +15%
互斥锁 ~50 ns ~5 μs ~20 μs +8%
读写锁(读) ~40 ns ~60 ns ~80 ns +5%

看到了吗?自旋锁在竞争激烈时延迟飙升,而且功耗增加最明显——因为失败的核在空转。互斥锁虽然延迟更大,但失败的核睡眠了,功耗反而低。

核心结论:没有银弹。选锁要看场景:短临界区用自旋锁,长临界区用互斥锁,读多写少用读写锁。而且,尽量减少锁的粒度和持有时间——这才是降低开销的根本。

下面这张图总结了多核同步的核心知识体系,我建议你保存下来反复看:

多核同步知识体系 多核架构 SMP 对称多处理 AMP 非对称多处理 BMP 混合架构 同步机制:自旋锁 · 互斥锁 · 读写锁 直接开销(指令延迟) 竞争开销(空转/调度) 伪共享(缓存行颠簸) 设计原则:短临界区 → 自旋锁 · 长临界区 → 互斥锁 · 读多写少 → 读写锁

嗯,这一章的内容就到这儿。记住:多核同步不是简单的"加把锁就完事",你得理解架构、选对机制、算清开销。后面咱们会深入每个锁的实现细节,还会讲怎么在低功耗约束下做同步优化。