单核处理器系统中 store buffer 的可见性 这里说了 单核情况下 一个示例程序 因为 两点的存在导致的 内存一致性问题
- store 指令 从 store buffer “出队” 的顺序 不能保证先入先出
- Store Buffer Forwarding 有限制条件,在部分情况下不能Forwarding
- 即 在 X86 下 ,单核不存在内存一致性问题 ,但在ARM/riscv 下,单核存在内存一致性问题
- 那么 我们将其 扩展为多核 ,如果 单核 存在内存一致性问题, 那么 多核照样存在内存一致性问题
不过我们 提出一个新的 场景 , 即多核场景下的 因为一个新的原因导致的 内存一致性问题,原因为
- 不保证写的顺序可见性
多核场景下,有cache,有store buffer
一开始内存中 ready 为 0 , data 为 0
// 用户函数 运行在 core 0
data = 123; // S1
ready = 1 ; // S2
// 用户函数 运行在 core 1
if (ready == 1) {
assert(data == 123);
}
此时我们假设 store 指令 按照 程序编码顺序 "出队", // 虽然在 弱一致性模型中,这个不能保证
那么 assert 会失败吗?
虽然 在core 0 而言指令 S1 和 S2 按照程序顺序提交,在core1看来其顺序未必相同
例如
对 ready 的写可能会较快的传播到处理器1(可能因为缓存命中)
而对 data 的写 可能会比较慢地传播到处理器1(可能因为缓存缺失)
🧠 反方观点1 : 我认为 不管快慢, 都是会读到的, 只会导致 流水线 stall 而已 , 所以 assert 不会失败
你说的“流水线 stall,等到读到再继续”,这种理解适用于单核或强一致模型(如 x86),但在 多核弱一致模型中,读取操作不会 stall 去等待一致性,而是读取当前可见的值:
✅ 在弱一致模型中(如 ARM/RISC-V):
Core 0
将data=123
写入其 store buffer → cache;- 紧接着
ready=1
也写入其 cache;
这两次写入虽然在 Core 0
看是顺序的,但:
Core 1
在访问ready
时,如果它的 cache 失效,会向系统请求ready
的缓存行,可能很快被刷新到了内存并响应;- 而
data
对应的缓存行可能还没刷新或系统没有通知Core 1
其缓存无效(Invalidate Miss); - 所以
Core 1
在这时看到了ready==1
,却还没看到data==123
。
这就出现了你说的那个令人惊讶的情况:
if (ready == 1) {
assert(data == 123); // 这里失败
}
✅ 为什么会这样?根本原因是内存模型
架构 | 是否允许这种乱序可见性(S2可见但S1不可见) |
---|---|
x86 | ❌ 不允许:Store 被强制顺序可见(TSO) |
ARM | ✅ 允许:Weak Ordering |
RISC-V | ✅ 允许:Weak Ordering |
所以在 ARM/RISC-V 上,除非你加上 memory barrier(内存屏障),否则确实可能出现:
- 写
data
还没传播出去, - 写
ready
已经传播到其他核, - 导致其他核看到不一致状态。
✅ 为什么不“stall”等待写都可见呢?
因为:
- 读取操作不是同步操作,不会等待写入同步完毕。
- 多核系统设计目标是并发性和性能,不会为了“等待另一个核心的数据刷新”而主动阻塞读取。
- 如果需要保证顺序,需要**人为添加屏障(memory barrier)**或用同步机制(如锁、原子操作)。
✅ 如果你希望 assert 不失败,该怎么做?
在 Core 0 上:
data = 123;
dmb(); // 数据内存屏障,确保 data 的写对其他核心可见
ready = 1;
或用 C11 原子写:
atomic_store_explicit(&data, 123, memory_order_relaxed);
atomic_store_explicit(&ready, 1, memory_order_release);
在 Core 1 上:
if (atomic_load_explicit(&ready, memory_order_acquire)) {
assert(atomic_load_explicit(&data, memory_order_relaxed) == 123);
}
这种用法利用了 release-acquire 同步语义,能确保 Core1 在看到 ready==1
后,也看到之前的 data
写。
✅ 总结
问题 | 是否成立 | 说明 |
---|---|---|
多核系统会 stall 直到看到一致的数据 | ❌ | 不会,读取会看到“当前可见的值”,不等于等待一致 |
store buffer 是 FIFO,所以写顺序一致 | ✅/❌ | 顺序进入 store buffer,但何时对其他核心可见不一定顺序 |
传播快慢是否只是影响延迟 | ❌ | 在弱一致模型下,传播差异导致可见顺序乱序,进而出现 assert 失败 |
assert(data == 123) 会失败吗 | ✅ 可能 | 在 ARM/RISCV 等架构上,确实可能,如果没有内存屏障 |