跳转至

可见性的硬件基础:寄存器、高速缓存、写缓冲器

一、为什么会有可见性问题

CPU 为了性能,并不直接读写内存,而是多层缓存:

CPU 寄存器 → L1 → L2 → L3 → 写缓冲器(Store Buffer) → 内存

二、寄存器

CPU 内部最快的存储,每个核有自己的寄存器。变量可能被加载到寄存器里,其他核看不到。

while (!flag) {
    // 如果 flag 被优化到寄存器,这个循环永远不退出
}

volatile 禁止这种优化,强制每次从内存读。

三、高速缓存

CPU 多级缓存。每个核有自己的 L1/L2,多核之间通过 MESI 保证一致性。

四、写缓冲器(Store Buffer)

CPU 写数据时,不是直接写到 L1,而是先写到一个写缓冲器里,再异步刷到缓存/内存。

为什么要写缓冲器

写操作慢(要等缓存一致性协议),CPU 不想等,先写缓冲器继续执行。

问题

写缓冲器是核内私有的!CPU 写完缓冲器就以为写成功了,但其他核还看不到。

核 A: a = 1;  ← 在写缓冲器里
核 A: b = 2;  ← 在写缓冲器里
核 B: 读 b=0, 读 a=0  ← 还没刷到内存

五、失效队列(Invalidate Queue)

MESI 的无效化请求是异步处理的。核 A 收到核 B 的无效化请求,先放到失效队列里,不是立即处理。

这导致:核 A 以为自己的缓存已经失效了,但实际还没处理,其他核读旧值。

六、和内存屏障的关系

  • StoreLoad 屏障:等写缓冲器排空。
  • StoreStore 屏障:保证前面的写都刷出去。
  • LoadLoad 屏障:让失效队列里的无效化生效。

这些屏障本质就是让 CPU 等一等,保证内存操作按顺序被其他核看到。

总结

可见性问题不是"CPU 忘了同步",而是硬件为了性能,把同步延迟了。内存屏障就是显式的"等一等"指令。