缓存一致性与 MESI:多核共享内存时缓存副本怎么保持一致
更新时间:2026-09-12。本文是「集成电路大类 · 芯片设计」**进阶层(intermediate)**数字逻辑第九十六篇。片上存储层次讲过缓存把常用数据放在核旁边加速。单核时没问题;多核时同一地址在多个私有缓存里各存一份,谁写了、别人手里的旧副本怎么办就是缓存一致性问题,它是多核 SoC 和 NoC上最重要的协议之一。
本文要回答的问题
- 多核各自缓存同一变量,一个核改了,另一个核为什么会读到旧值?
- 「写无效」和「写更新」两种策略,为什么主流选写无效?
- M/E/S/I 四个状态分别表示什么?E 和 S 差在哪、为什么要分?
- 一次写命中、写缺失在总线上发什么消息,别人的状态怎么变?
- 监听协议为什么在很多核时扩展不动?目录协议怎么解决?coherence 和 consistency 是一回事吗?
一、问题:一份数据,多个副本
核0 ─ L1/L2 ─┐
核1 ─ L1/L2 ─┼── 互连 ── 共享 LLC/内存 同一行 X 在 0、1 都有副本
核2 ─ L1/L2 ─┘若核0 写 X=1 只更新自己的缓存,核1 再读 X 仍命中旧值 0。一致性协议保证:对同一缓存行,所有核的读写表现得像只有一份按序更新的数据。注意它管的是**缓存行(cache line,常见 64B)**粒度,哪怕只改一个字节,也要对整行做一致性处理——这引出后面的假共享。
二、两大策略与两大组织
- 写无效(write-invalidate):写之前先把其它核的副本全部作废,之后只有写者拥有可写副本,写多次只广播一次。主流协议都用它;
- 写更新(write-update/broadcast):每次写都把新值广播去更新所有副本,流量大,少用;
- 监听(snooping/bus):所有缓存挂在共享总线上,每个一致性事务广播给大家,各自监听并响应。实现简单、延迟低,但广播带宽随核数平方增长、靠总线有序,适合中小规模(多核 CPU 常见);
- 目录(directory):在 home 节点为每行记录「谁持有副本、谁是脏拥有者」,点对点发无效而不全局广播,可扩展到几十上百核(常跑在 mesh NoC上),代价是目录存储和多跳延迟。
三、MESI 四态
每行在每个缓存里处于四态之一:
| 态 | 名称 | 副本是否有效 | 是否唯一 | 是否被改脏 |
|---|---|---|---|---|
| M Modified | 有效 | 仅我有 | 脏(与内存不同,我负责写回) | |
| E Exclusive | 有效 | 仅我有 | 干净(与内存一致) | |
| S Shared | 有效 | 可能多核共有 | 干净、只读 | |
| I Invalid | 无效 | — | — |
关键迁移(写无效、总线监听):
- 读缺失:发
BusRd。别人没有→进 E(独占干净,之后写可直接升级、无需再广播);别人有 S 或 M→大家进/留 S,脏的 M 先写回提供数据; - 写命中:若在 E/M 直接写(E→M,不产生总线流量,这就是区分 E、S 的价值);若在 S,发
BusRdX/BusUpgr请求独占并无效化他人,别人 S→I,自己→M; - 写缺失:发
BusRdX,把其它副本作废、取来数据后进 M; - 监听到别人对我持有的行发 RdX:我的 S/E→I,或 M→I 前先写回(可同时给请求者)。
E 与 S 分开的意义:若某行只有自己一份且干净(E),本地写不必先发广播无效(没有别人可无效),省一次总线事务;S 态写才必须先升级。
四、假共享、MOESI 与一致性 ≠ 一致性模型
- 假共享(false sharing):协议以整缓存行(64B)为单位,两个核频繁改写同一行里不同变量也会互相无效、乒乓抖动,性能暴跌却与逻辑无关;软件上靠对齐/填充把各核热变量放到不同行解决;
- MOESI:加 **O(Owned)**态,允许某核持有一份「脏但可共享」的负责副本、向别人传数据而不必立刻写回内存,减少写回流量;还有加 Forward 态的变种;
- 目录扩展:核很多时广播监听在 NoC 上代价大,目录按行记录共享者列表做点对点无效(与 NoC 路由、AXI/ACE 或 CHI 协议配合);
- coherence ≠ consistency:缓存一致性(coherence)保证同一地址多个副本读到一致值;**内存一致性模型(consistency,如 TSO/弱序)**规定不同地址读写在多核间可见的顺序,是更上层的规则,别混淆。原子指令/锁、DMA 与缓存的同步维护也都建立在一致性机制上。
与现有篇目的衔接
- 前置:片上存储/缓存层次、存储器分类/存储体、AMBA/AXI
- 配套:NoC 与可扩展互连、Chiplet 跨 die 一致性、中断/DMA 缓存维护
- 相关:原子操作/寄存器、FIFO/互连、SoC 架构
总结
一句话总结:多核各有私有缓存时同一内存行会有多份副本,一个核改写后别人可能读到旧值,缓存一致性协议就是让这些副本对外表现得像一份按序更新的数据;主流采用写无效策略(写前先把别人副本作废,多次写只广播一次),实现上分挂共享总线、所有缓存监听广播事务的 snooping(简单低延迟但带宽随核数增长)和在 home 节点记录共享者、点对点发无效的 directory(可扩展到大核数、常跑在 mesh NoC 上);经典 MESI 给每行标注 M 独占且脏并负责写回、E 独占且干净、S 可能共享只读、I 无效四态,读缺失发 BusRd 据他人有无进入 E 或 S,写命中在 E/M 可直接写而在 S 必须发升级/无效广播把他人打成 I 再进入 M,分出 E 与 S 正是为了独占干净行的本地写省一次广播;工程上要警惕因按整缓存行(64B)管理而让不同核的相邻热变量互相无效的假共享,需要时用 MOESI 的 O 态减少写回;最后务必区分缓存一致性 coherence 与规定不同地址操作跨核可见顺序的内存一致性模型 consistency,原子操作、锁以及 DMA 前后的缓存清理/回填都依赖这套机制。