为了解决这个缓存不一致的问题,我们就需要有一种机制,来同步两个不同 CPU 的缓存数据。那这样的机制需要满足什么条件呢?我觉得能够做到下面两点就是合理的。
写传播(Write Propagation):在一个 CPU 核心里,我们的 Cache 数据更新,必须能够传播到其他的对应节点的 Cache Line 里。
事务的串行化(Transaction Serialization):在一个 CPU 核心里面的读取和写入,在其他的节点看起来,顺序是一样的。
事务的串行化比较难理解,举个例子。有 CPU-1~4 四个 CPU,CPU-1 将数据 i 修改成 5000,而 CPU-2 将数据 i 修改成 6000,它们都将修改的操作广播给其余两个 CPU。CPU-3 可能先收到 CPU-1 的广播,将 i 修改成 5000,而接着又收到 CPU-2 的广播,将 i 修改成 6000,而 CPU-4 恰好相反,先修改成 6000 再修改成 5000。这样就造成这两个 CPU 数据的不一致,也就是没做到事务的串行化。
2. 总线嗅探机制和 MESI 协议
要解决缓存一致性问题,首先要解决的是多个 CPU 之间的数据传播问题。
- 总线嗅探(Bus Snooping):最常见的一种解决多核 CPU 数据广播问题的方案。本质上就是把所有的读写请求都通过总线(Bus) 广播给所有的 CPU 核心,然后让各个核心去“嗅探”这些请求,再根据本地的情况进行响应。
- MESI 协议:基于总线嗅探机制的缓存一致性协议,MESI 协议也是在 Pentium 时代被引入到 Intel CPU。
2.1 写传播 vs 写失效
- 写失效(Write Invalidate) 协议:在写失效协议里,在这个 CPU 写入 Cache 后,它会去广播一个“失效”请求给其他所有 CPU。其他 CPU 如果也存在这个缓存行,直接标记成失效的就好了。MESI 协议使用的就是写失效协议。
- 写传播(Write Invalidate) 协议:会将数据广播给其他 CPU,相对于写失效请求,占用更多的带宽。
2.2 MESI 协议
MESI 协议的由来呢,来自于我们对 Cache Line 的四个不同的标记,分别是:
- M:代表已修改(Modified),表示数据修改后未同步到主存的脏数据。
- E:代表独占(Exclusive),独占模式下,可以对应数据任意修改,不需要广播给其它 CPU。
- S:代表共享(Shared),当有两个 CPU 读取数据时,缓存行的状态从独占变成共享。此时修改数据时,需要广播给其他 CPU。
- I:代表已失效(Invalidated),表示数据已经失效,需要重新从主存中读取数据。