x86架构WB内存场景下是否需要使用内存屏障?
结论:你的结论是错误的——LFENCE必须保留,SFENCE在WB内存下确实多余,但不能直接说俩都不用
先把咱们的场景再理一遍:
- WB内存环境里初始
a = b = 0 - P1跑的代码:
a = 1→SFENCE→b = 1 - P2跑的代码:
WHILE (b == 0) {}→LFENCE→ASSERT (a == 0)
结合x86的TSO(总存储顺序)内存模型,咱们一步步掰扯:
1. SFENCE在WB内存下确实没必要留
x86对WB(回写型)内存的存储操作有硬规则:同一个处理器里的写操作,绝对不会打乱程序顺序重排。也就是说P1写完a=1之后,才会写b=1,其他处理器看到的这俩写操作的顺序和P1的代码顺序完全一致。所以这里的SFENCE确实是多余的,这部分你判断对了。
2. LFENCE是真的不能省,你漏了处理器的“小聪明”
你忽略了x86处理器的推测性加载行为——虽然x86保证同一个处理器里的读操作不会乱序,但它会偷偷提前执行读操作,哪怕后面的逻辑要依赖前面读的结果。
放到P2的代码里看:
- 处理器在死循环读
b的时候,可能会提前“预判”循环迟早会退出,就先去读了a的值(这时候a还是0),然后把这个值存起来。 - 等循环真的退出(检测到
b=1)的时候,处理器懒得再读一次a,直接用之前存的0,结果ASSERT(a==0)就触发了——但这时候P1早就把a=1写到内存里了,完全不符合咱们预期的逻辑。
LFENCE就是来治这个“小聪明”的:它强制后面的读操作(也就是读a)必须等前面的读操作(死循环读b)彻底完成之后才能执行,确保读a的时候,已经确定b=1了,这时候读到的a肯定是P1写的1,ASSERT就不会瞎触发。
最后总结
你对SFENCE的判断是对的,但漏了推测执行带来的坑,LFENCE必须保留,所以“俩屏障都不用”的结论是错的。
内容的提问来源于stack exchange,提问作者Kay
相关产品推荐
相关产品推荐

