2、多核架构基础:SMP与AMP概念解析、多核处理器分类(同构/异构)、缓存一致性基础

好,咱们正式开始聊多核。说实话,我第一次接触多核开发时,脑子里全是问号。一个核都够难伺候了,多个核一起跑,不会打架吗?嗯,带着这个疑问,我们一步步拆解。

2.1 什么是SMP?什么是AMP?

这两个缩写,你面试时大概率会被问到。我当年第一次听到,还以为是某种音频格式……开个玩笑。

SMP(对称多处理),说白了就是:多个核心跑同一个操作系统,共享同一份内存,大家地位平等。你想想看,就像几个程序员共用一台服务器,谁都能访问全局变量,谁都能调度任务。

我个人习惯把SMP想象成「合租」—— 大家共用客厅(内存),共用厨房(外设),但各有各的房间(寄存器、缓存)。

AMP(非对称多处理)则完全不同。每个核心跑自己的操作系统,甚至可能是裸机程序。核心之间不共享内存,或者只共享一小块约定的区域。这就像「独栋别墅」—— 各家过各家的日子,偶尔串个门。

核心区别一句话总结:

  • SMP:一个系统,多个核心,共享内存
  • AMP:多个系统,多个核心,各自为政

我在项目中遇到过这样一个场景:客户要求一个芯片同时跑Linux和实时控制任务。Linux那边要网络、文件系统,实时任务要求微秒级响应。怎么办?AMP方案就派上用场了 —— 一个核跑Linux,另一个核跑裸机或RTOS,中间通过共享内存通信。

2.2 多核处理器分类:同构 vs 异构

搞清楚了SMP和AMP,咱们再看处理器的分类。这个其实更直观。

同构多核:所有核心一模一样。比如你买了个8核的CPU,8个Cortex-A76,完全对称。这种芯片天生适合SMP。Zephyr在支持同构多核时,基本就是开箱即用。

异构多核:核心类型不同。最常见的就是ARM的big.LITTLE架构 —— 大核(高性能)加小核(低功耗)。还有更极端的,比如Cortex-A系列加Cortex-M系列,一个跑Linux,一个跑RTOS。

类型 典型场景 Zephyr支持方式
同构SMP 手机AP、服务器CPU 原生SMP支持,多核调度
异构AMP MCU+MPU混合芯片 OpenAMP框架,rpmsg通信
异构SMP big.LITTLE架构 需要自定义调度策略

我记得有一次调试一个异构芯片,Cortex-A核跑Linux,Cortex-M核跑Zephyr。两边通过共享内存传数据,结果M核写的数据A核读出来全是乱的。后来发现是缓存一致性问题 —— 嗯,这就引出了下一个重点。

2.3 缓存一致性基础

为什么需要缓存一致性?你想想看,每个核心都有自己的L1缓存。核心A修改了变量x,但修改只写到了自己的缓存里,没写回主存。核心B去读x,读到的还是旧值。这就出问题了。

在SMP系统中,这个问题尤其严重。因为所有核心共享内存,但各自有私有缓存。硬件上通常用MESI协议(或它的变种)来解决。MESI代表四种状态:

  • M(Modified):缓存行被修改,与主存不一致
  • E(Exclusive):只有当前核心有这个缓存行,且与主存一致
  • S(Shared):多个核心都有这个缓存行,且与主存一致
  • I(Invalid):缓存行无效,需要重新加载

硬件帮你做了大部分工作,但软件层面也有坑。我曾经踩过一个雷:两个核通过共享内存通信,A核写数据后发中断通知B核。B核读数据时,因为缓存没刷新,读到的还是旧值。解决方案是在写数据后加一条内存屏障指令(比如ARM的dmb),强制缓存同步。

避坑指南:

我曾经在Zephyr的AMP项目中,忘记在共享内存操作前后加k_cache_flush(),结果数据同步时好时坏。排查了整整两天才发现是缓存捣的鬼。记住:跨核共享数据,一定要考虑缓存一致性!

在Zephyr中,如果你使用SMP模式,内核已经帮你处理了大部分缓存一致性问题。但如果你做AMP开发,尤其是通过共享内存通信时,就需要手动处理了。Zephyr提供了arch_mem_flush()arch_mem_invalidate()等API,专门用于这种场景。

2.4 小结

这一章我们聊了三个核心概念:

  • SMP和AMP的区别 —— 合租 vs 独栋
  • 同构和异构的分类 —— 一样的内核 vs 不一样的内核
  • 缓存一致性问题 —— 硬件帮你兜底,但软件也要留个心眼

下一章我们会深入Zephyr的SMP实现,看看内核是怎么调度任务到不同核心上的。到时候我会拿一个实际的双核Cortex-M芯片做演示,保证让你看明白。

注意: 如果你用的是异构AMP方案,千万别指望Zephyr自动帮你搞定核间通信。你需要自己搭建共享内存区域,定义通信协议。Zephyr的OpenAMP框架可以帮你省不少事,但底层原理一定要懂。