上篇文章中介绍了CPU高速缓存的写入、写回策略,并引申出多核情况下缓存不一致的问题,CPU要怎么解决这个问题的呢?
缓存不一致问题
沿用上一张的图,再来看一下缓存不一致的问题,有一个账户有金额600元,在时刻1时此金额数据已经同时存在于核1和核2中,时刻2时,账户金额-300,请求打到核1上,核1的高速缓存的金额数据-300,余额变为300,数据仍在缓存中未刷回内存。时刻3时,账户金额-100,请求打到了核2上,核1的高速缓存的金额数据-100,余额变为500,数据仍在缓存中未刷回内存。后面如果核1或核2的缓存数据需要写回内存时,无论哪个核写回,这个金额数据对正常业务流程来说,都是错误的。
这个问题如何解决呢?我们来想想方案
写传播与事务串行化
每次业务请求进来CPU后,CPU如果要修改数据,需要将其他核心里的同一个缓存行的数据锁住, 修改完后需要将结果数据同步给其他核心,然后解锁。这种方案就是写传播,每个核心修改数据后需要将本次修改传播给其它核心,保证数据的一致性。而加锁的概念是为了事务的串行化,不能说两个核心可以同时修改一个数据,这样并发操作公共变量,就乱套了。
那么计算机是如何实现写传播与事务串行化的呢?
总线嗅探
使用总线嗅探机制就可以实现写传播,什么是总线嗅探呢?先来看看什么是总线。
总线可以理解为是计算机主板上的一条主干线,它连接了计算机的各个重要部件,如CPU、内存、硬盘及其他各种输入输出设备,使各个部件之间都可以通过总线来进行通信,CPU与CPU之间,核心与核心之间都通过总线来进行通信。
CPU的一个核心修改了数据后,往总线上发一条消息,广播给所有核心。各个核心都在嗅探着总线上的消息,接到消息后再根据自身情况进行处理。这就是总线嗅探机制。
基于总线嗅探机制,就可以定义一种协议来实现事务的串行化了。
写失效与写广播
写失效与写广播是两种协议类型。
写失效:一个核心负责写数据,写完某个缓存行的数据之后,向其他核心广播一个此缓存行“失效”的消息,其他核心收到消息后将对应的缓存行置为“失效”状态。
写广播:一个核心负责写数据,写完某个缓存行的数据之后,将此缓存行的数据广播给其他核心,其他核心收到消息后将自己的相应缓存行的数据做同步修改。
可以看到,写广播的方式实现起来比较简单,就是我们上面讲到的“写传播”,但是由于广播中要携带数据,所以很占总线的带宽。写失效的广播数据中携带的信息就比较少,只要带着被修改的缓存行的地址就行了。MESI协议用的就是写失效协议。
MESI协议
MESI协议是一种用于多处理器计算机系统中缓存一致性的协议。它的名字是根据其四种缓存状态而来:Modified(已修改)、Exclusive(独占)、Shared(共享)和Invalid(无效)。
- Modified(已修改): 表示缓存行已被当前处理器修改,但尚未写回到主内存。其他处理器的缓存中的相同数据是无效的。如果其他处理器试图读取这个数据,会导致缓存失效。
- Exclusive(独占): 表示缓存行的数据与主内存中的数据匹配,且没有其他处理器缓存这个数据。这是一个干净的缓存行,其他处理器可以读取它而不会引起冲突。
- Shared(共享): 表示缓存行的数据与主内存中的数据匹配,并且可能存在其他处理器也缓存了相同的数据。多个处理器可以同时读取这个数据而不会引起冲突。
- Invalid(无效): 表示缓存行无效,即它的数据已过期或无效。任何试图访问这个无效数据的操作都需要从主内存中重新获取最新的数据。
MESI协议的主要目标是确保多个处理器之间对共享内存的访问保持一致性,同时最大程度地减少了对主内存的访问,从而提高了性能。它通过跟踪缓存中的数据状态并使用相应的状态转换来实现这一目标。如果一个处理器需要访问数据,它首先会检查该数据的状态,然后根据状态采取相应的操作,以确保数据一致性。
MESI的四种状态之间的流转可以看做是一个有限状态机,下面是状态流转示意图
连线说明:
- 蓝线:来自于当前核心内部发生的操作
- 红线:来自于其他核心广播出来的信号
操作与信号说明:
由当前核心内部发生的操作都叫做本地xxx,由其他核心通过总线发出来的信号都叫做总线xxx。
- 本地读:当前核心内部去读取一个缓存行的数据
- 本地写:当前核心内部去修改一个缓存行的数据
- 发出总线读信号:当前核心在读取一个缓存行的同时,向总线广播一个读信号
- 发出总线写信号:当前核心在修改一个缓存行的同时,向总线广播一个写信号
- 总线读:其他核心正在读取一个缓存行的数据
- 总线写:其他核心正在修改一个缓存行的数据
- 发出写回信号:当前核心往总线广播一个缓存行写回内存的信号
有了MESI协议,各个核心之间就可以有条不紊的工作了。
java里有个volatile关键字用来保证多线程之间变量的可见性,那么volatile和MESI协议干的事是不是一样呢,他俩是什么关系? 网上搜了一些文章看了看,MESI协议是多核计算机内置的一个协议,volatile只是有可能会用上这个协议来实现它的目的,但是单单MESI协议是完成不了volatile的语义的。
总结
这篇文章中围绕“缓存不一致问题”的解决方案,讨论了写传播和事务串行化的解决方法,总线嗅探机制实现了写传播,在此基础上,MESI协议又通过协调各核心的缓存行状态实现了事务串行化,最终打造出了多核计算机的缓存一致性方案。