为何负载-负载控制依赖需完整读内存屏障?内核文档疑问
背景
在Linux内核文档Documentation/memory-barriers.txt:709中,存在如下代码片段,其中要求在标注位置插入完整读内存屏障,官方给出的原因是:CPU可能提前预测结果来短路执行,导致其他CPU看到b的读取早于a的读取:
q = READ_ONCE(a); if (q) { <read barrier> // why? p = READ_ONCE(b); }
问题1:该说明是否意味着执行此代码片段的CPU不会重排a和b的读取操作?
不是。这个说明恰恰是在说执行代码的CPU本身可能会重排a和b的读取顺序——CPU会通过预测if(q)的结果为真,提前去读取b,导致实际执行中b的读取可能先于a的读取完成,或者从其他CPU的视角看,b的读操作被观测到的时间早于a。插入读屏障就是为了阻止这种重排。
问题2:其他CPU看到的读取顺序为何重要?请举例说明会导致bug的场景。
多CPU场景下,内存操作的可见顺序直接影响数据一致性。举个典型的生产者-消费者场景:
- 生产者CPU先设置
b = 1(准备好数据),再设置a = 1(标记数据就绪) - 消费者CPU执行上述代码,本应先读
a,确认非0后再读b获取有效数据
如果没有读屏障,消费者CPU提前读取了b(此时b可能还是0),之后才读取到a=1,就会错误地把旧的b=0当成有效数据使用,导致逻辑错误。
问题3:内核支持的CPU是否允许这类读取重排?
是的。比如ARM、PowerPC等弱内存模型的CPU,本身就允许读操作的重排;即使是x86这种强内存模型的CPU,虽然不会重排普通的读-读操作,但CPU的推测执行(比如提前加载)会导致从其他CPU视角看,读操作的顺序被打乱,本质上和重排的效果一致,所以同样需要屏障来约束。
问题4:若允许,该规则的出处在哪里?
这个规则直接来源于Linux内核官方文档Documentation/memory-barriers.txt,尤其是其中关于Speculative Loads(推测加载)和Read Barriers的章节。文档明确说明,即使是强内存模型架构,推测执行也可能引发跨CPU的读顺序异常,必须通过读屏障来确保读取操作的顺序一致性。
内容的提问来源于stack exchange,提问作者Bob

