4. MESI 状态转换详解:本地读/写、远程读/写触发的状态迁移图与总线事务
MESI 协议的核心在于每个 Cache Line 的四种状态(Modified、Exclusive、Shared、Invalid)在本地与远程读写操作下的精确迁移。理解这些迁移路径以及伴随的总线事务,是 ARM 多核缓存一致性移植适配的基础。本章通过状态迁移图、总线事务表与典型场景分析,详细拆解每一种转换路径。
4.1 MESI 状态定义回顾
| 状态 | 缩写 | 含义 | 数据有效性 | 与主存一致性 | 其他核副本 |
|---|---|---|---|---|---|
| Modified | M | 已修改 | 有效 | 不一致(脏) | 无 |
| Exclusive | E | 独占 | 有效 | 一致(干净) | 无 |
| Shared | S | 共享 | 有效 | 一致(干净) | 可能有 |
| Invalid | I | 无效 | 无效 | — | — |
关键概念: M 状态是“脏”的,E 和 S 状态是“干净”的。只有 M 状态的 Cache Line 在被替换或远程读取时需要写回主存。
4.2 本地读/写触发的状态迁移
本地操作是指当前核(Local Core)访问自己的 Cache。总线事务仅在需要获取数据或通知其他核时产生。
4.2.1 本地读(Local Read, LR)
- I → E/S: Cache Line 无效,发起
BusRd总线事务。若其他核无副本,则进入 E 状态;若其他核有 Shared 副本,则进入 S 状态。 - S → S: 命中,状态不变,无总线事务。
- E → E: 命中,状态不变,无总线事务。
- M → M: 命中,状态不变,无总线事务。
4.2.2 本地写(Local Write, LW)
- I → M: Cache Line 无效,发起
BusRdX总线事务,通知其他核无效化其副本。数据加载后直接修改,标记为 M。 - S → M: 当前为 Shared,发起
BusUpgr总线事务,使其他核副本无效化。数据修改后进入 M。 - E → M: 当前为 Exclusive,无总线事务(因为本核独占),直接修改数据进入 M。
- M → M: 命中,直接修改,状态不变,无总线事务。
ARM 移植要点: 在 ARM Cortex-A 系列中,
BusRdX 和 BusUpgr 在 ACE(AXI Coherency Extensions)总线上分别映射为 ReadUnique 和 CleanUnique 事务。移植时需要确保总线桥接逻辑正确转换这些事务类型。
4.3 远程读/写触发的状态迁移
远程操作是指其他核(Remote Core)访问当前核持有的 Cache Line。当前核作为“监听者”响应总线事务。
4.3.1 远程读(Remote Read, RR)
- M → S: 远程核发起
BusRd,当前核提供数据(Cache-to-Cache 传输),数据写回主存,状态降级为 S。 - E → S: 远程核发起
BusRd,当前核提供数据(或主存提供),状态降级为 S。 - S → S: 远程核发起
BusRd,状态不变。 - I → I: 无效状态,无影响。
4.3.2 远程写(Remote Write, RW)
- M → I: 远程核发起
BusRdX,当前核提供数据并写回主存,状态变为 I。 - E → I: 远程核发起
BusRdX,状态变为 I。 - S → I: 远程核发起
BusRdX或BusUpgr,状态变为 I。 - I → I: 无效状态,无影响。
4.4 MESI 状态迁移全景图
下图展示了所有本地与远程操作触发的状态转换路径。箭头上的标签表示触发事件。
4.5 总线事务类型与触发条件
| 总线事务 | 触发操作 | 事务方向 | 数据源 | 对监听核的影响 |
|---|---|---|---|---|
| BusRd | 本地读 I 状态 | 请求核 → 总线 | 主存或其他 Cache | M→S, E→S, S 不变 |
| BusRdX | 本地写 I 状态 | 请求核 → 总线 | 主存或其他 Cache | M→I, E→I, S→I |
| BusUpgr | 本地写 S 状态 | 请求核 → 总线 | 无数据(仅无效化) | S→I |
ARM 移植警告: 在 ARMv8 架构中,
BusUpgr 对应 ACE 协议的 CleanUnique 事务。如果总线桥接实现错误地将 CleanUnique 当作 ReadUnique 处理,会导致不必要的 Cache Line 回写,造成性能损失。务必在移植时区分这两种事务的语义。
4.6 典型场景:Cache Line 状态迁移流程
以下通过一个 4 核系统的示例,展示一个 Cache Line 从 I 状态开始,经历多次读写操作后的完整状态变化。
- Core 0 本地读(I→E): Core 0 发起
BusRd,其他核无副本,进入 E 状态。 - Core 1 本地读(E→S, I→S): Core 1 发起
BusRd,Core 0 监听到后降级为 S,Core 1 进入 S。 - Core 0 本地写(S→M): Core 0 发起
BusUpgr,Core 1 的 S 状态变为 I,Core 0 进入 M。 - Core 2 远程读(M→S, I→S): Core 2 发起
BusRd,Core 0 提供数据并写回主存,降级为 S,Core 2 进入 S。 - Core 3 远程写(S→I, S→I, M→I): Core 3 发起
BusRdX,所有其他核的副本无效化,Core 3 进入 M。
性能观察: 步骤 4 中 M→S 的降级触发了 Cache-to-Cache 传输和写回,延迟较高。在 ARM 多核系统中,频繁的 M→S→M 振荡会导致“一致性抖动”,应通过软件数据布局(如核间私有数据与共享数据分离)来避免。
4.7 状态迁移与总线事务对照表
| 当前状态 | 操作 | 下一状态 | 总线事务 | 数据流 |
|---|---|---|---|---|
| I | 本地读 | E / S | BusRd | 主存/Cache → 请求核 |
| I | 本地写 | M | BusRdX | 主存/Cache → 请求核 + 无效化 |
| S | 本地读 | S | 无 | — |
| S | 本地写 | M | BusUpgr | 无效化其他核 |
| E | 本地读 | E | 无 | — |
| E | 本地写 | M | 无 | — |
| M | 本地读 | M | 无 | — |
| M | 本地写 | M | 无 | — |
| M | 远程读 | S | BusRd | 当前核 → 请求核 + 写回主存 |
| E | 远程读 | S | BusRd | 当前核/主存 → 请求核 |
| S | 远程读 | S | BusRd | 主存/其他核 → 请求核 |
| M | 远程写 | I | BusRdX | 当前核 → 请求核 + 写回主存 |
| E | 远程写 | I | BusRdX | 请求核获取数据 |
| S | 远程写 | I | BusRdX / BusUpgr | 请求核获取数据或仅无效化 |
4.8 ARM 移植中的状态转换注意事项
- M 状态写回时机: 在 ARM 实现中,M 状态 Cache Line 被远程读时,必须保证数据写回主存后再响应
BusRd,否则会导致数据不一致。ACE 协议通过WriteBack和WriteClean事务区分是否需要保留 Cache 副本。 - E 状态优化: E 状态是 ARM 性能优化的重点。本地写 E 状态无需总线事务,这是单线程性能的关键。移植时应确保 Cache 控制器在分配新行时优先尝试 E 状态。
- S 状态无效化延迟: 远程写触发 S→I 时,ARM 处理器通常使用“监听无效化”机制。如果监听延迟过高,可能导致写操作等待。在移植中,建议使用“写更新”队列来隐藏无效化延迟。
- 总线事务合并: 在 ARM 多核集群中,多个核同时写同一 Cache Line 时,总线控制器应合并
BusUpgr事务,避免广播风暴。
移植经验: 在将 MESI 协议移植到 ARM Cortex-A76 集群时,我们发现 E→M 的无总线事务路径对 SPEC CPU 性能贡献约 8-12%。确保 Cache 控制器在本地写命中 E 状态时不产生任何 snoop 请求,是移植性能达标的关键。