最近在深入分析volatile关键字的实现原理,内存屏障、缓存一致性、store buffer、invalid queue等知识看了不少,但接触的资料比较零散,无法完整的把相关内容串联起来,所以抽空做些整理,也算对这段时间的学习做一个小结。
众所周知,volatile最主要的特性是保证了变量的可见性,即任意线程总能读到对volatile变量的最后写入(最新值)。而可见性正是依靠缓存一致性、内存屏障等机制协同运作来保证的。下面先就volatile与缓存一致性协议之间的关系分享一点个人理解。
缓存一致性
计算机的存储结构在内存和CPU之间存在多级缓存,有些为CPU共享,如L3缓存;有些为CPU独享,如L1,L2缓存,缓存一致性问题即发生在独享缓存。引入多核后,一个数据可能在多个CPU的独享缓存存在拷贝,当其中一个CPU修改了本地缓存后,该数据在其他CPU缓存行的数据成为脏数据,相同数据出现了不一致。缓存一致性协议即为了保证多核场景下内存数据的在cpu cache的一致性。
MESI协议
MESI代表了缓存行的四种不同状态,状态标记以及触发状态转换的消息通知机制保证了缓存行的数据一致性。
状态:
已修改Modified (M):缓存行是脏的(dirty),与主存的值不同。如果别的CPU内核要读主存这块数据,该缓存行必须回写到主存,状态变为共享(S).
独占Exclusive (E):缓存行只在当前缓存中,但是干净的(clean)–缓存数据同于主存数据。当别的缓存读取它时,状态变为共享;当前写数据时,变为已修改状态。
共享Shared (S):缓存行也存在于其它缓存中且是干净的。缓存行可以在任意时刻抛弃。
无效Invalid (I):缓存行是无效的。这也是初始状态。
消息:
Read: CPU发起读取数据请求,请求中包含需要读取的数据地址。
Read Response: 作为Read消息的响应,该消息可能是内存响应的,也可能是某CPU响应的(比如该地址在某CPU Cache Line中为Modified状态,该CPU必须返回该地址的最新数据)。