|
linux
、
计算机组成
内存屏障随笔(memory-barrier)
Store Buffer(write barrier)
MESI 协议中,如果 CPU-0
对一个块 非独占数据进行修改 会发出 Invalid 信号通知其他持有该数据的 CPU
,然后等待 CPU
ACK。如果其他 CPU
ACK 响应较慢,则会浪费 CPU-0
大量的 CPU时间
,所以有了 Store Buffer
。
处理器把它想要写入到主存的值写到缓存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。但是这么做会有问题:
广播延迟,导致结果异常
下图中:
CPU 0 步驟如下:
- 写入 a 的值 (=1) 到 store buffer (但 cache 里仍是 0)
- 广播 “invalidate a” (异步)
- 改 b 的值。因为 b 的状态已是
Modified
,cacheline
的值更新成 1
CPU 1 步骤如下:
- 本地读 b,发现是 invalid
- CPU-0 将 b 刷入主存
- 读取主存中 b 的值
- 此时 invalid a 广播 还没到达
- 读取 a 的值(此时 a 的状态是 shared,所以直接读 0)到寄存器
- 断言 a 的值为 1 失败(此时 a 为 0)
解决方案
问题出在了,store buffer 破坏了 缓存一致性(cache coherence)。于是 CPU 需要提供 write memory barrier
,让软件在必要场景(data-race)避免这个问题。
下图中:
CPU 0 步驟如下:
- 写入 a 的值 (=1) 到 store buffer (但 cache 里仍是 0)
- 广播 “invalidate a” (异步)
- write barrier 指令阻塞,直到获取 invalid ack
- 改 b 的值。因为 b 的状态已是
Modified
,cacheline
的值更新成 1
CPU 1 步骤如下:(5,6)两步发生在 b 等于 1 之前
- 本地读 b,发现是 invalid
- CPU-0 将 b 刷入主存
- 读取主存中 b 的值
- 如果此时 b 为 0 则跳回 步骤1
- 收到 invalid a 广播
- a 设置成 invalid
- 发送 invalid ack 到 总线(BUS)
- 读取 a 的值 发现是 invalid
- CPU-0 将 a 刷入主存
- 读取主存中 a 的值(1)
- 断言 a 的值为 1 成功(此时 a 为 1)