内存屏障
问题产生原因:
cpu0执行写操作,若CPU本地cache没有此变量,须发一个invalidate到总线,其他CPU收到invalidate消息后将此变量从自己的本地cache中清除,并且发送ack给CPU0,CPU0收到其他CPU发送的ack后将变量值写入到本地cache,但是CPU0在等待其他CPU回复ack时是出于停滞状态,大部分时间都是在等待消息,为了解决此问题引入CPU本地stroed buff。
stroed buff引入使的CPU0无需等待其他CPU的答复先把值写入本地stroed buff,等其他CPU回复ack后再把变量值写入本地cache;收到读请求后先放入本地无效化队列,在写入cpu cache
这两个问题导致了数据可见性:1、数据写入时,先到store buff,而没有到cpu 本地cache,更到不了内存,大量数据写入时候本地store buff数据乱序,并且其他cpu看不见此数据,因为此时在本地store buff中还没后到cache,EMSI协议无效;2、读数据时如果收到read invalid消息放入本地无效化队列,此时再读取数据是属于脏数据,其他cpu修改的数据导致本地不可见
引入stroed buff后,变量读写情况为:
a = 0 , b = 0; a = 1; b = a + 1; assert(b == 2);
a在CPU1的本地cache中,b在CPU0的本地cache中
- CPU0写变量a,但是CPU0本地cache中没有此变量,发送read invalidate到总线上,让CPU1清除本地cache中a的值
- CPU0把a的值写入本地stroed buff
- CPU1收到read invalidate消息后清除本地cache中a的值并回应CPU0 ack消息
- CPU0执行b=a+1;
- CPU0收到CPU1回复的消息中a为0,从本地cache中读取a == 0
- CPU0执行a+1,写入b,由于b被CPU0独占直接写入本地cache,此时b的值为1
- b == 2 报错
造成这个问题原因是由于CPU对内存进行操作的时候顺序和程序代码执行指令顺序不一致造成的;另一个原因是由于同一个CPU中stroed buff和本地数据不一致造成的;解决本地CPU数据顺序不一致可以由stroed forwarding处理,当CPU进行读操作时从stroed buff和本地cache中读取数据,若stroed buff中有数据则使用stroed buff的。
内存操作顺序
a = 0 , b = 0; void fun1() { a = 1; b = 1; } void fun2() { while (b == 0) continue; assert(a == 1); }
CPU 0 执行 fun1() , CPU 1 执行 fun2() , a 变量在 CPU 1 cache 中 , b 变量在 CPU 0 cache 中
- CPU0执行a=1,由于不在本地cache中,把a=1放入stroed buff中,发送read invalidate到总线上
- CPU1执行while循环,由于b不在CPU1本地cache中,发送read message到总线上,从其他CPU或memory中获取b的值
- CPU0执行b=1,由于b在本地cache(cacheline处于modified状态或者exclusive状态)中,直接写入cache中
- CPU0收到cpu1发送的read message后,把最新值1送给CPU1,同时将b cacheline的状态设定为shared
- CPU1收到CPU0的read response后,把b=1写入本地cache,修改状态为shared
- 由于b值等于1了,因此CPU 1跳出while (b == 0)的循环,继续执行
- CPU 1执行assert(a == 1),这时候CPU 1的local cache中还是旧的a值,因此assert(a == 1)失败
- CPU1收到CPU0的read invalidate后,以变量a的值进行回应,清空本地cache中a的值
- CPU0收到CPU1的read response、ack后,将stroed buff中a的值写入本地cache
由于CPU0对a的值写入还没有完成,CPU1开始对a的值进行读取;因为CPU并不知道哪些变量有相关性,这些变量是如何相关的。