概述
如图是多核CPU和主存通信的一个大致模型,其中的cache是用来解决cpu和主存速度不匹配的问题,所以现如今cpu主要是和它的cache做数据交互,不会和主存直接交互。
这也出现了缓存不一致的问题,比如cpu1在主存中读取一个数据,并且修改了它的值,但是修改后的值只是放在了自己的缓存中,并没有实时的更新到主存中,这时候cpu2来访问注册中的对应的数据,拿到的是一个未修改后的值,这就导致了程序的错误。
解决方法
1.总线加锁
在总线上加LOCK#,如cpu1从主内存读取数据到自己的高速缓存中,会在总线对这个数据加锁,这样其它cpu没法去读或写这个数据,直到cpu1使用完数据释放锁之后其它cpu才能读取该数据。这是早期cpu实现缓存一致性的解决方案,但是这种方法太耗损性能,把并行执行硬生生变为了并发执行,已经被淘汰了,了解即可。
2.MESI协议
对单个缓存行的数据进行加锁,不会影响到内存中其他数据的读写,锁的范围变小了效率也相应的提高了。
高速缓存中每一个缓存行都有四个状态,分别是M(Modifield)修改、E(Exclusive)独享、S(Shared)共享、I(Invalid)无效,这也是MESI名称的由来。
M:代表该缓存行中的内容被修改了,并且该缓存行只被缓存在该CPU中。这个状态的缓存行中的数据和内存中的不一样,在未来的某个时刻它会被写入到内存中。
E:代表该缓存行对应内存中的内容只被该CPU缓存,其他CPU没有缓存该缓存对应内存行中的内容。这个状态的缓存行中的内容和内存中的内容一致。该缓存可以在任何其他CPU读取该缓存对应内存中的内容时变成S状态。或者本地处理器写该缓存就会变成M状态。
S:该状态意味着数据不止存在本地CPU缓存中,还存在别的CPU的缓存中。这个状态的数据和内存中的数据是一致的。当有一个CPU修改该缓存行对应的内存的内容时会使该缓存行变成 I 状态。
I:代表该缓存行中的内容是无效的。
MESI状态转移图
local read和local write分别代表本地CPU读写。remote read和remote write分别代表其他CPU读写。
当前为M转态时
事件 | 行为 | 下一状态 |
---|---|---|
local read | 当前cpu从它的cache中读,且只有当前cpu使用到了该数据,状态肯定不变 | M |
local write | 当前cpu修改它的cache中写,状态也不变 | M |
remote read | 这行数据必须要先同步到内存,其他cpu再继续读到最新的数据, 因为同时被多个cpu共享,所以其转为共享状态 | S |
remote write | 同上,确保内存的数据为最新数据,但由于其他cpu改变了最新数据, 所以本cpu缓存中的数据不是最新的了,需要丢弃,设为无效状态 | I |
当前为E状态时
事件 | 行为 | 下一状态 |
---|---|---|
local read | 读取本地缓存的数据,且没有改变数据,所以还是独享状态 | E |
local write | 修改了当前缓存的值,所以和内存中的值不一致了,但是因为其他线程 没有使用到该数据,转为修改状态 | M |
remote read | 因为E状态的特点不需要先同步数据到内存。数据被其他cpu读到,所以 变为共享状态 | S |
remote write | 由于其他cpu改变了最新数据,设为无效状态 | I |
当前为S状态时
事件 | 行为 | 下一状态 |
---|---|---|
local read | 没有改变值,同样为共享状态 | S |
local write | 修改了当前缓存的值,把其他cpu的值变为无效转态,当前cpu的值变为 修改状态,表示只有本线程独有且和内存中的数据不一致 | M |
remote read | 因为E状态的特点不需要先同步数据到内存。数据被其他cpu读到,所以 变为共享状态 | S |
remote write | 数据被修改,本地缓存行的数据变为无效状态 | I |
当前为I状态时
事件 | 行为 | 下一状态 |
---|---|---|
local read | 1.如果其他处理器中没有这份数据,本缓存从内存中取该数据,状态变为E 2.如果其他处理器中有这份数据,且缓存行状态为M,则先把缓存行中的内容写 回到内存。本地cache再从内存读取数据,这时两个cache的状态都变为S 3.如果其他缓存行中有这份数据,并且其他缓存行的状态为S或E,则本地cache 从内存中取数据,并且这些缓存行的状态变为S | E或S |
local write | 1.先从内存中取数据,如果其他缓存中有这份数据,且状态为M,则先将数据 更新到内存再读取 2.如果其他缓存中有这份数据,且状态为E或S,那么其他缓存行的状态变为I | M |
remote read | remote read不影响本地cache的状态 | I |
remote write | remote read不影响本地cache的状态 | I |
总结
MESI协议本质就是:多个cpu从主内存读取同一个数据到各自的高速缓存,当其中某个cpu修改了缓存里的数据,该数据会马上同步回主内存,其它cpu通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效。