内存屏障

问题产生原因:

  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中

  1. CPU0写变量a,但是CPU0本地cache中没有此变量,发送read invalidate到总线上,让CPU1清除本地cache中a的值
  2. CPU0把a的值写入本地stroed buff
  3. CPU1收到read invalidate消息后清除本地cache中a的值并回应CPU0 ack消息
  4. CPU0执行b=a+1;
  5. CPU0收到CPU1回复的消息中a为0,从本地cache中读取a == 0
  6. CPU0执行a+1,写入b,由于b被CPU0独占直接写入本地cache,此时b的值为1
  7. 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 中

  1. CPU0执行a=1,由于不在本地cache中,把a=1放入stroed buff中,发送read invalidate到总线上
  2. CPU1执行while循环,由于b不在CPU1本地cache中,发送read message到总线上,从其他CPU或memory中获取b的值
  3. CPU0执行b=1,由于b在本地cache(cacheline处于modified状态或者exclusive状态)中,直接写入cache中
  4. CPU0收到cpu1发送的read message后,把最新值1送给CPU1,同时将b cacheline的状态设定为shared
  5. CPU1收到CPU0的read response后,把b=1写入本地cache,修改状态为shared
  6. 由于b值等于1了,因此CPU 1跳出while (b == 0)的循环,继续执行
  7. CPU 1执行assert(a == 1),这时候CPU 1的local cache中还是旧的a值,因此assert(a == 1)失败
  8. CPU1收到CPU0的read invalidate后,以变量a的值进行回应,清空本地cache中a的值
  9. CPU0收到CPU1的read response、ack后,将stroed buff中a的值写入本地cache

  由于CPU0对a的值写入还没有完成,CPU1开始对a的值进行读取;因为CPU并不知道哪些变量有相关性,这些变量是如何相关的。

原文地址:https://www.cnblogs.com/tianzeng/p/14998876.html