‘壹’ 缓存一致性指的是什么
首先明白什么是缓存,缓存是介于物理存储与CPU处理之间的一段内存空间,主要用于存储从物理存储读出、或者要写入的数据,这需要硬件或者软件支持。如果读取或写入物理存储中的一个字节或一段数据,如果没有缓存,那么每次的读写请求都会直接访问物理存储,而物理存储的速度一般都比较慢,而且物理定位也比较慢,缓存使用后,可以一次性读出需要的数据相邻的数据,暂时存储在缓存中,下面如果还要读取,而这部分数据已经在缓存了,就不需要再去读取物理存储,同样,如果是写操作,可以先将需要写入的数据暂时保存在缓存中,等到缓存过期或者强行清空时,再一次写入物理存储。这样可以把多次的物理存储访问,变成一次物理存储的访问,提高访问效率。具体的操作算法这里不多作阐述。
缓存的一致性就是指缓存中的数据是否和目标存储中的数据是一样的,也就是说缓存中已经修改得数据是否已经保存到了物理存储中,物理存储中已经被修改得内容,是否与缓存的内容是一样的。这就是一致性的概念。
‘贰’ CPU写Cache时内容不一致现象,有那两种解决方法各自的优缺点是什么
有两种方法写回法(抵触修改法):是在CPU执行写操作时,信息只写入 Cache,仅当需要被替换时,才将已被写入过的 Cache块先送回主嫌橡存,然后再调入新块。
写直芹配旁达法(直达法):利用 Cache-主存存储层次在处理机和主存之间的直接通路,每当处理机写入 Cache的同时,也通过此通路直接写入主存在可靠性上,写直达法优于写回法。
在与主存的通信量上,写回法少于写直达法;在控制的复杂性上,写直达法比写回法简单;在硬件实现的代价上,写回法要比写直达法好。
(2)cpu缓存一致性协议缺点扩展阅读:
由于cache的内容只是主存部分内容的拷贝,它应当与主存内容保持一致。但是,在一些情况下会出现cache和主存内容不一致的情况。如下:
1、 写操作后,没有刷洗cache到内存里面,那么cache为脏,跟内存不同步。
2、多CPU模式。 一个CPU修改了本地cache,那么其它CPU的cache全部失效。
3、DMA访问。DMA修改了内存中的值,cache中仍然为原卖弊值,错误。
‘叁’ CPU Cache
title: CPU Cache
date: 2019-11-17 20:20:30
keywords: cache "CPU cache" "三级缓存" 缓存映射 cache原理 多级cache TLB
引入 Cache 的理论基础是程序局部性原理,包括时间局部性和空间局部性。时间局部性原理即最近被CPU访问的数据,短期内CPU 还要访问(时间);空间局部性即被CPU访问的数据附近的数据,CPU短期内还要访问(空间)。因此如果将刚刚访问过的数据缓存在一个速度比主存快得多的存储中,那下次访问时,可以直接从这个存储中取,其速度可以得到数量级的提高。
CPU缓存是(Cache Memory)位于CPU与内存之间的临时存储器,它的容量比内存小但交换速度快。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。
在CPU中加入缓存是一种高效的解决方案,是对于存储器件成本更低,速度更快这两个互相矛盾的目标的一个最优解决方案,这样整个内存储器(缓存+内存)就变成了既有缓存的高速度,又有内存的大容量的存储系统了。缓存对CPU的性能影响很大,主要是因为CPU的数据交换顺序和CPU与缓存间的带宽引起的。
下图是一个典型的存储器层次结构,我们可以看到一共使用了三级缓存
各级存储访问延迟的对比
介于CPU和主存储器间的高速小容量存储器,由静态存储芯片SRAM组成,容量较小但比主存DRAM技术更加昂贵而快速, 接近于CPU的速度。CPU往往需要重复读取同样的数据块, Cache的引入与缓存容量的增大,可以大幅提升CPU内部读取数据的命中率,从而提高系统性能。通常由高速存储器、联想存储器、地址转换部件、替换部件等组成。如图所示。
早期采用外部(Off-chip)Cache,不做在CPU内而是独立设置一个Cache。现在采用片内(On-chip)Cache,将Cache和CPU作在一个芯片上,且采用多级Cache,同时使用L1 Cache和L2 Cache,甚至有L3 Cache。
上图显示了最简单的缓存配置。它对应着最早期使用CPU cache的系统的架构。CPU内核不再直接连接到主内存。所有的数据加载和存储都必须经过缓存。CPU核心与缓存之间的连接是一种特殊的快速连接。在一个简化的表示中,主存和高速缓存连接到系统总线,该系统总线也可用于与系统的其他组件进行通信。我们引入了系统总线(现代叫做“FSB”)。
引入缓存后不久,系统变得更加复杂。高速缓存和主存之间的速度差异再次增大,使得另一个级别的高速缓存不得不被添加进来,它比第一级高速缓存更大且更慢。出于经济原因,仅增加第一级缓存的大小不是一种选择。今天,甚至有机器在生产环境中使用了三级缓存。带有这种处理器的系统如图下所示。随着单个CPU的内核数量的增加,未来的缓存级别数量可能会增加。现在已经出现了拥有四级cache的处理器了。
上图展示了三级缓存的结构。L1d是一级数据cache,L1i是一级指令cache。请注意,这只是一个示意图; 现实中的数据流从core到主存的过程中不需要经过任何更高级别的cache。CPU设计人员有很大的自由来设计cache的接口。对于程序员来说,这些设计选择是不可见的。
另外,我们有拥有多个core的处理器,每个core可以有多个“线程”。核心和线程之间的区别在于,独立的核心具有所有硬件资源的独立的副本,早期的多核处理器,甚至具有单独的第二级缓存而没有第三级缓存。核心可以完全独立运行,除非它们在同一时间使用相同的资源,例如与外部的连接。另一方面,线程们共享几乎所有的处理器资源。英特尔的线程实现只为线程提供单独的寄存器,甚至是有限的,还有一些寄存器是共享的。
一个现代CPU的完整概貌如图所示。
由于cache中对应的都是主存地址,即物理地址,在cqu查看具体数据是否在cache中时,如果CPU传送过来的地址时一个虚拟地址,需要将其转换成实际物理地址再到cache中去寻找。Cache的实现需要TLB的帮助。可以说TLB命中是Cache命中的基本条件。TLB不命中,会更新TLB项,这个代价非常大,Cache命中的好处基本都没有了。在TLB命中的情况下,物理地址才能够被选出,Cache的命中与否才能够达成。
TLB是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存。TLB是位于内存中的页表的cache,如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。
当cpu对数据进行读请求时,CPU根据虚拟地址(前20位)到TLB中查找.TLB中保存着虚拟地址(前20位)和页框号的对映关系,如果匹配到虚拟地址就可以迅速找到页框号(页框号可以理解为页表项),通过页框号与虚拟地址后12位的偏移组合得到最终的物理地址.
如果没在TLB中匹配到虚拟地址,就出现TLB丢失,需要到页表中查询页表项,如果不在页表中,说明要读取的内容不在内存,需要到磁盘读取.
TLB是MMU中的一块高速缓存,也是一种Cache.在分页机制中,TLB中的数据和页表的数据关联,不是由处理器维护,而是由OS来维护,TLB的刷新是通过装入处理器中的CR3寄存器来完成.如果MMU发现在TLB中没有命中,它在常规的页表查找后,用找到的页表项替换TLB中的一个条目.
当进程进行上下文切换时重新设置cr3寄存器,并且刷新tlb.
有两种情况可以避免刷tlb.
第一种情况是使用相同页表的进程切换.
第二种情况是普通进程切换到内核线程.
lazy-tlb(懒惰模式)的技术是为了避免进程切换导致tlb被刷新.
当普通进程切换到内核线程时,系统进入lazy-tlb模式,切到普通进程时退出该模式.
cache是为了解决处理器与慢速DRAM(慢速DRAM即内存)设备之间巨大的速度差异而出现的。cache属于硬件系统,linux不能管理cache.但会提供flush整个cache的接口.
cache分为一级cache,二级cache,三级cache等等.一级cache与cpu处于同一个指令周期.
CPU从来不从DRAM直接读/写字节或字,从CPU到DRAM的每次读或写的第一步都要经过L1 cache,每次以整数行读或写到DRAM中.Cache Line是cache与DRAM同步的最小单位.典型的虚拟内存页面大小为4KB,而典型的Cache line通常的大小为32或64字节.
CPU 读/写内存都要通过Cache,如果数据不在Cache中,需要把数据以Cache Line为单位去填充到Cache,即使是读/写一个字节.CPU 不存在直接读/写内存的情况,每次读/写内存都要经过Cache.
缓存里有的数据,主存中一定存在。
一级缓存中还分数据缓存(data cache,d-cache)和指令缓存(instruction cache,i-cache)。二者分别用来存放数据和执行这些数据的指令,而且两者可以同时被cpu访问,所以一级cache间数据时独立的。
一级没有的数据二级可能有也可能没有。因为一级缓存miss会接着访问二级缓存。
一级有二级一定有,三级也一定有。因为一级的数据从二级中读上来的。在一级缺失二级命中时发生。
二级没有的数据三级可能有也可能没有。因为二级确实会接着访问三级缓存。找不到会继续访问主存。
二级有的数据三级一定有。在二级缺失三级命中时,数据会从三级缓存提到二级缓存。
三级没有的数据,主存可能有也可能没有。三级缓存缺失,会访问主存,主存也缺失就要从外存访问数据了。
三级缓存有的数据主存一定有。因为在三级缺失主存命中时,数据会从主存提到三级缓存中来。
一级缓存就是指CPU第一层级的高速缓存,主要是为了缓存指令和缓存数据,一级缓存的容量对CPU性能影响非常大,但是因为成本太高,所以一般容量特别小,也就256KB左右。
二级缓存是CPU第二层级的高速缓存,对于CPU来说,二级缓存容量越大越好,它是直接影响CPU性能的,CPU每个核心都会有自己的缓存,一个CPU的二级缓存容量是所有核心二级缓存容量的总和。
三级缓存就是CPU第三层级的高速缓存,主要是为了降低与内存进行数据传输时的延迟问题,三级缓存与一二级不同,三级缓存只有一个,它是所有核心共享,所以在CPU参数中可以看到,三级缓存相对于其他两级缓存来说都很大。
由于缓存的设置与OS无关且透明,所以对于不同的体系架构下不同的处理器对待缓存区域的处理和方式都不同,不同的处理器也有不同的缓存设置值。从主流的处理器cache大小来看,一般一个cache line的大小都是固定的64B左右,这是经过经验得到的比较合理的大小,一般一级cache大小在数十KB左右,二级cache大小在数十到数百KB左右,而L3 cache大小在数MB左右。
由于三级cache一般来说是运用于拥有多核的处理器,对于单核处理器来说二级cache就能够足够保持够高的cache命中率。所以一般的三级cache一般只针对于多核处理器。L1和L2级cache是处理器核所单独的内容。L1又可以看成是L2的cache。L2可以看成是L3级cache的cache。所以我们分两个部分讨论数据放置与数据淘汰策略。
各级cache间的数据放置策略主要有三种。直接相连映射,全相联映射和组相联映射。将一个主存块存储到唯一的一个Cache行。对应的大小都是一个cache line的大小,一般来说是64B。
多对一的映射关系,但一个主存块只能拷贝到cache的一个特定行位置上去。cache的行号i和主存的块号j有如下函数关系:i=j mod m(m为cache中的总行数)。
可以将一个主存块存储到任意一个Cache行。
主存的一个块直接拷贝到cache中的任意一行上
可以将一个主存块存储到唯一的一个Cache组中任意一个行。
将cache分成u组,每组v行,主存块存放到哪个组是固定的,至于存到该组哪一行是灵活的,即有如下函数关系:cache总行数m=u×v 组号q=j mod u
组间采用直接映射,组内为全相联。硬件较简单,速度较快,命中率较高。是现代处理器中一般所常用的映射方式。
Cache工作原理要求它尽量保存最新数据,当从主存向Cache传送一个新块,而Cache中可用位置已被占满时,就会产生Cache替换的问题。
常用的替换算法有下面三种。
LFU(Least Frequently Used,最不经常使用)算法将一段时间内被访问次数最少的那个块替换出去。每块设置一个计数器,从0开始计数,每访问一次,被访块的计数器就增1。当需要替换时,将计数值最小的块换出,同时将所有块的计数器都清零。
这种算法将计数周期限定在对这些特定块两次替换之间的间隔时间内,不能严格反映近期访问情况,新调入的块很容易被替换出去。
LRU(Least Recently Used,近期最少使用)算法是把CPU近期最少使用的块替换出去。这种替换方法需要随时记录Cache中各块的使用情况,以便确定哪个块是近期最少使用的块。每块也设置一个计数器,Cache每命中一次,命中块计数器清零,其他各块计数器增1。当需要替换时,将计数值最大的块换出。
LRU算法相对合理,但实现起来比较复杂,系统开销较大。这种算法保护了刚调入Cache的新数据块,具有较高的命中率。LRU算法不能肯定调出去的块近期不会再被使用,所以这种替换算法不能算作最合理、最优秀的算法。但是研究表明,采用这种算法可使Cache的命中率达到90%左右。
最简单的替换算法是随机替换。随机替换算法完全不管Cache的情况,简单地根据一个随机数选择一块替换出去。随机替换算法在硬件上容易实现,且速度也比前两种算法快。缺点则是降低了命中率和Cache工作效率。
处理器微架构访问Cache的方法与访问主存储器有类似之处。主存储器使用地址编码方式,微架构可以地址寻址方式访问这些存储器。Cache也使用了类似的地址编码方式,微架构也是使用这些地址操纵着各级Cache,可以将数据写入Cache,也可以从Cache中读出内容。只是这一切微架构针对Cache的操作并不是简单的地址访问操作。为简化起见,我们忽略各类Virtual Cache,讨论最基础的Cache访问操作,并借此讨论CPU如何使用TLB完成虚实地址转换,最终完成对Cache的读写操作。
Cache的存在使得CPU Core的存储器读写操作略微显得复杂。CPU Core在进行存储器方式时,首先使用EPN(Effective Page Number)进行虚实地址转换,并同时使用CLN(Cache Line Number)查找合适的Cache Block。这两个步骤可以同时进行。在使用Virtual Cache时,还可以使用虚拟地址对Cache进行寻址。为简化起见,我们并不考虑Virtual Cache的实现细节。
EPN经过转换后得到VPN,之后在TLB中查找并得到最终的RPN(Real Page Number)。如果期间发生了TLB Miss,将带来一系列的严重的系统惩罚,我们只讨论TLB Hit的情况,此时将很快获得合适的RPN,并依此得到PA(Physical Address)。
在多数处理器微架构中,Cache由多行多列组成,使用CLN进行索引最终可以得到一个完整的Cache Block。但是在这个Cache Block中的数据并不一定是CPU Core所需要的。因此有必要进行一些检查,将Cache Block中存放的Address与通过虚实地址转换得到的PA进行地址比较(Compare Address)。如果结果相同而且状态位匹配,则表明Cache Hit。此时微架构再经过Byte Select and Align部件最终获得所需要的数据。如果发生Cache Miss,CPU需要使用PA进一步索引主存储器获得最终的数据。
由上文的分析,我们可以发现,一个Cache Block由预先存放的地址信息,状态位和数据单元组成。一个Cache由多个这样的Cache Block组成,在不同的微架构中,可以使用不同的Cache Block组成结构。我们首先分析单个Cache Block的组成结构。单个Cache Block由Tag字段,状态位和数据单元组成,如图所示。
其中Data字段存放该Cache Block中的数据,在多数处理器微架构中,其大小为32或者64字节。Status字段存放当前Cache Block的状态,在多数处理器系统中,这个状态字段包含MESI,MOESI或者MESIF这些状态信息,在有些微架构的Cache Block中,还存在一个L位,表示当前Cache Block是否可以锁定。许多将Cache模拟成SRAM的微架构就是利用了这个L位。有关MOESIFL这些状态位的说明将在下文中详细描述。在多核处理器和复杂的Cache Hierarchy环境下,状态信息远不止MOESIF。
RAT(Real Address Tag)记录在该Cache Block中存放的Data字段与那个地址相关,在RAT中存放的是部分物理地址信息,虽然在一个CPU中物理地址可能有40,46或者48位,但是在Cache中并不需要存放全部地址信息。因为从Cache的角度上看,CPU使用的地址被分解成为了若干段,如图所示。
这个地址也可以理解为CPU访问Cache使用的地址,由多个数据段组成。首先需要说明的是Cache Line Index字段。这一字段与Cache Line Number类似,CPU使用该字段从Cache中选择一个或者一组Entry。
Bank和Byte字段之和确定了单个Cache的Data字段长度,通常也将这个长度称为Cache 行长度,上图所示的微架构中的Cache Block长度为64字节。目前多数支持DDR3 SDRAM的微架构使用的Cache Block长度都是64字节。部分原因是由于DDR3 SDRAM的一次Burst Line为8,一次基本Burst操作访问的数据大小为64字节。
在处理器微架构中,将地址为Bank和Byte两个字段出于提高Cache Block访问效率的考虑。Multi-Bank Mechanism是一种常用的提高访问效率的方法,采用这种机制后,CPU访问Cache时,只要不是对同一个Bank进行访问,即可并发执行。Byte字段决定了Cache的端口位宽,在现代微架构中,访问Cache的总线位宽为64位或者为128位。
剩余的字段即为Real Address Tag,这个字段与单个Cache中的Real Address Tag的字段长度相同。CPU使用地址中的Real Address Tag字段与Cache Block的对应字段和一些状态位进行联合比较,判断其访问数据是否在Cache中命中
如果cache miss,就去下一级cache或者主存中去查找数据,并将查找到的数据采用上面的数据淘汰策略将数据替换到cache中。
由于在发生cache miss时会产生数据替换,在运行过程中缓存的数据也可能会被修改。所以需要一个策略来保持数据在缓存和主存间的一致性。
Cache写机制分为write through和write back两种。
‘肆’ 缓存一致性协议
锁缓存行有一套协议叫做 缓存一致性协议 。缓存一致性协议有MSI、MESI、MOSI、Synapse、Firefly以及DragonProtocol等等。
MESI分别代表缓存行数据的4中状态,通过对这四种状态的切换,来达到对缓存数据进行管理的目的
假设有三个CPU-A、B、C,对应三个缓存分别是cache-a、b、c。在主内存中定义了x的引用值0
单核读取
MESI优化和引入的问题:各CPU缓存行的状态是通过消息传递来进行的。如果CPU0要对一个在缓存中共享的变量进行写入,首先需要发送一个失效的消息给到其他缓存了该数据的CPU,并且要等到他们的确认回执。CPU0在这段时间内都会一直处于阻塞状态,会导致各种各样的性能问题和稳定性问题。
为了避免阻塞带来的资源浪费,在CPU中引入了Store Buffer。
CPU在写入共享数据时,直接把数据写入到Store Buffer中,同时发送Invalidate消息,然后继续去处理其他指令。当收到其他所有CPU发送了Invalidate Acknowledge消息时,再将Store Buffer中的数据存储到Cache Line中,最后再从Cache Line同步到主内存。
‘伍’ EMSI协议
翻译至 https://en.wikipedia.org/wiki/MESI_protocol
EMSI是基于缓存无效化的一致性缓存协议,并且是一种最常见的支持回写式缓存的协议。它也被称为伊利诺伊州协议(由于其在伊利诺伊大学厄本那香槟分校被开发)。回写式缓存和写入式缓存相比可以节约很多的带宽。回写式缓存经常会存在脏状态,而脏状态表明了高数缓存中的数据与主存中的数据不一致。EMSI要求当缓存未命中时并且别的缓存中有该数据,那么缓存和缓存间应该互相传输数据。MESI相对与MSI来说降低了与主存的交互次数,这带来了显着的性能提升。
MESI的四个字母分别代表了四个可以被标记在缓存行上的独立状态。(也就是用2bit来编码)
当缓存行处于Modified状态时,它表明该缓存行只存在于当前缓存中。并且这个缓存行中的数据是脏数据,也就是说这个缓存行中的数据与主存中的数据不一致。缓存被要求在未来将缓存行的数据写于主存中,但不用立即写入。但如果别的缓存向该缓存请求这个数据,那必须保证该数据写入主存已经完成。当回写回主存完成后,缓存行状态会有Modified变为Shared状态。
当缓存行处于Exclusive状态时,它表明该缓存行只存在于当前缓存中,不过其中的数据与主存中的数据是一致的。当别的缓存向该缓存请求read当前缓存行时,缓存行状态会变成Shared。或者当有write操作时,他会变成Modified状态。
当缓存行处于Shared状态时,它表明该缓存行可能同时存在与别的缓存中,并且其中的数据与主存中一致。这个缓存行随时可能被丢弃(改变为Invalid状态)。
当缓存行处于Invalid 状态时,表明该缓存行是无效的。
对于给定的两个缓存,以下是允许共同存在的状态:
当一个缓存中的变量被标记为M状态,那同样拥有这个变量的别的缓存的缓存行会被标记为Invalid。
从一个状态到另一个状态的转变有两个重要影响因素。第一个因素是处理器发出了特殊的读写请求。举个栗子:处理器A的缓存中有变量X,然后这个处理器向自己的缓存发送了对于这个变量的读写请求。第二个影响因素是来自别的处理器的请求,这些处理器缓存中没有这个变量,或者它们想要更新这个变量。这些总线请求被一个名叫Snoopers的监听器监听着。
下面解释不同种类的处理器请求和总线请求
处理器请求包括如下两种:
总线请求包括如下五种:
如果从主存中获取一个值需要等待的时间比通过缓存间传值等待的时间长,那么我们就可以说缓存间传值可以降低缓存未命中后的读延迟。在多核架构下,一致性主要在二级缓存间保持,但处理器仍然有三级缓存,从三级缓存中获取未命中的变量往往快于从二级缓存。
监控操作 :
所有缓存中的变量都拥有四种状态的有限状态机。
对于不同输入的状态转换关系和回复如下表1.1和1.2
只有写操作发生在Modified或Exclusive状态的缓存行时,才缓存才不需要做额外操作。如果状态为Shared的缓存行被写入,那么首先别的缓存需要无效它们的缓存行。这个由一个叫 Request For Ownership (RFO) .的广播操作执行。
当一个变量为Modified时,这个缓存必须监听所有别的缓存对于该变量对应主存的读请求,一旦监听到就必须将这个变量写入主存。这个过程可以通过强制让别的缓存等待的方法来完成。当完成了对主存的写入,状态变为Shared。但同样可以直接通知别的缓存这个变量的值,而不写入主存。
当处于Shared状态时,必须监听所有的rfo广播,一旦收到就让缓存中的变量无效。
Modified和Exclusive状态是精确的,当有缓存行处于这个状态就表明,这个变量是这个缓存独有的。而Shared状态时不精确的,当别的缓存丢弃了这个Shared状态的缓存行,那么可能只存在一个缓存行状态为Shared但它不会变为Exclusive。丢弃Shared状态行不会引起别的缓存的注意。
使用Exclusive可以带来性能上的提升,因为修改Exclusive不需要通知别的缓存,而修改Shared状态的缓存行需要告诉别的缓存,并使这些缓存行无效,这是耗时的。(这也是EMSI与MSI协议的区别)
MESI简单直接的实现存在着两个性能问题。1.当向一个Invalid的缓存行写入时,需要从别的缓存获取值,这是很耗时的。2.需要将别的缓存行的该值设置为Invalid这也是耗时的。为了解决这两个问题,我们引入了存储缓冲区和无效化队列。
当向一个无效的缓存行写入时会用到储存缓冲区。因为这个写操作最终一定会被执行,因此CPU发送读无效消息(让别的缓存中的该缓存行无效)并且将要写入的值放到存储缓存区中,当这个需要的缓存行写入缓存时,存储缓存区中存储的值将被执行写入。
存储缓存区存在带来的后果是,当一个CPU进行写操作,值不会立即写入缓存中。因此,无论何时CPU读取缓存中的值时,都需要先扫描自己的存储缓冲区,确保缓冲区中是否还有未写入的值。值得注意的时不同CPU之间存储缓冲区时不可见的。
当CPU收到无效请求时,它会将这写无效请求放入无效化队列中。放入队列中的请求会被迅速执行但不是立即执行。所以CPU可以忽略它的缓存块是无效的。CPU不能扫描它的无效队列,这是和存储缓冲区的区别。
可见存储缓冲区带来的是写不同步,而无效化队列则带来读不同步,为了解决这些不同步,我们需要内存屏障。写屏障会刷新我们的存储缓冲区,确保里面所有的写都会被执行到这个CPU的cache上,而读屏障可以保证所有无效化队列里的任务都被执行,确保别的CPU的写对自己可见。