MESI协议 #
我们先来了解 MESI 协议中,处理器对缓存的请求:
- PrRd:处理器请求从缓存块中读出;
- PrWr:处理器请求向缓存块写入。
而总线对缓存的请求分别是:
- BusRd:总线侦听到一个来自另一个处理器的读出缓存请求;
- BusRdX:总线侦听到来自另一个尚未取得该缓存块所有权的处理器读独占(或者写)缓存的请求;
- BusUpgr:侦听到一个其他处理器要写入本地缓存块上的数据的请求;
- Flush:总线侦听到一个缓存块被另一个处理器写回到主存的请求;
- FlushOpt:侦听到一个缓存块被放置在总线以提供给另一个处理器的请求,和 Flush 类似,但只不过是从缓存到缓存的传输请求。
缓存块的状态分为 4 种,也是 MESI 协议名字的由来:
- Modified(M):缓存块有效,但是是“脏”的,其数据与主存中的原始数据不同,同时还表示处理器对于该缓存块的唯一所有权,表示数据只在这个处理器的缓存上是有效的;
- Exclusive(E):缓存块是干净有效且唯一的;
- Shared(S):缓存块是有效且干净的,有多个处理器持有相同的缓存副本;
- Invalid(I):缓存块无效。
同样,我们用状态机来表示缓存块状态的变化,如下图所示:

在上图中,“/”前表示的是请求,这个请求可能来自 CPU 自己,也可能来自总线,“/”后表示的是当前请求所引起的总线事件,“-”表示不产生总线事件。
这个状态机看起来比较复杂,首先,图中的黑色箭头表示是由当前处理器发起的,红色箭头表示,这个事件是从总线来的,也就是由其他处理器发起的。
我们先看由处理器发起的请求(黑线部分):
- M 状态:读写操作都不会改变状态,并且因为能够确定不会有其他副本,因此不会产生任何总线事务;
- E 状态:任何对该缓存块的读操作都会缓存命中,且不触发任何总线事务。一个对 E 状态的写操作,也不会产生总线事务,只需将缓存块状态改为 M;
- S 状态:当处理器读时,缓存命中,不产生总线事务。当处理器写时,需要产生 BusUpgr 事件,通知其他处理器我要写这个缓存块,并将缓存块状态置为 M;
- I 状态:当处理器发出读请求时,遇到缓存块缺失,要把数据加载进缓存,产生一个 BusRd 总线请求。内存控制器响应 BusRd 请求,将所需要的缓存块从内存中取出,同时会检查有没有其他处理器也有该缓存块拷贝,如果发现拷贝则将状态置为 S, 并且把其他有拷贝的处理器的状态也相应地置为 S;如果没有发现其他拷贝,则将状态置为 E。
接下来,我们看下由总线发起的请求(红色部分):
- M 状态:该缓存块是整个系统里唯一有效的,并且内存的数据也是过时的。因此当侦听到 BusRd 时,缓存块必须被清空以保证写传播,所以会产生 Flush 事件。并且将状态置为 S。当侦听到 BusRdX 时,也必须产生 Flush 事件,因为有其他处理器要写,所以当前缓存块置为 I;
- E 状态:当侦听到 BusRd 请求时,说明另一个处理器遇到了缓存缺失,并试图获取该缓存块,因为最终的结果是要将这个缓存块,放在不止一个处理器缓存上,所以状态必须被置为 S。这样就会产生 FlushOpt 事件,来完成缓存到缓存的传输。当 BusRdX 被侦听到时,说明有其他处理器想要独占这个缓存块上的数据,这种情况下,本地缓存块将会被清空并且状态需要置为 I,同时也会产生 FlushOpt 事件,完成缓存到缓存的传输,将当前数据的最新值同步给需要进行写操作的其他处理器。而当侦听到 BusUpgr 时,说明其他处理器要写当前处理器持有的缓存副本,所以要将状态置为 I,但是不必产生总线事务;
- S 状态:当侦听到 BusRd 时,也就是另一个处理器遇到缓存缺失而试图获取该缓存块,因为 S 状态本身是共享的,所以状态保持 S 不变;
- I 状态:侦听到的 BusRd、BusRdX、BusUpgr 都不会影响它,所以忽略该情况,状态保持不变。
总体来讲,MESI 协议通过引入了 Modified 和 Exclusive 两种状态,并且引入了处理器缓存之间可以相互同步的机制,非常有效地降低了 CPU 核间带宽。它是当前设计中进行 CPU 核间通讯的主流协议,被广泛地使用在各种 CPU 中。