我最近阅读了内存屏障中cpu的传递性,作者强调只有一般屏障才能保证传递性。但是,我不能很好地理解它。例如:
CPU 1 CPU 2 CPU 3
======================= ======================= =======================
{ X = 0, Y = 0 }
STORE X=1 LOAD X STORE Y=1
<read barrier> <general barrier>
LOAD Y LOAD X
假设X在CPU3的缓存中,状态被修改;Y在CPU2的缓存中,状态也被修改。
如果我们在读屏障之前添加写屏障,CPU1 与 CPU2 共享它的存储缓冲区。(它成为一个普遍的障碍)
1) CPU1 在存储缓冲区中设置 X(X=1) 的值。
2) CPU2 从存储缓冲区(共享存储缓冲区)中读取 X 的值。
3)CPU2在存储缓冲区(写屏障)中标记X,并读取无效队列以确保没有来自CPU3的无效消息(读屏障)。
4) CPU2 想要将 X 的缓存行从无效更改为已修改,因此向 CPU3 发送无效消息。
5) CPU3接收到X的无效消息,将其放入无效队列并响应CPU2。
6) CPU2收到response后,将X = 1写入内存或缓存,并加载Y == 0。
...
7) CPU3在执行通用屏障时会发现它的无效队列中有X的无效消息,之后X必须等于1。
没关系,我可以理解。但是,我从perbook的图14.3中阅读了另一个示例,如下所示:
thread0(void) {
A = 1;
smp_wb();
B = 1;
}
thread1(void) {
while (B == 0)
continue;
barrier();
C = 1;
}
thread2(void) {
while (C == 0)
continue;
barrier();
assert(A == 1);
}
有一些机会触发断言。作者说将所有障碍更改为 smp_mb 可以在 Quick Quiz 14.2 的回答中修复它。
所以,我的问题是为什么我们需要将 thread1 中的屏障更改为 smp_mb?如果 thread0 和 thread1 在 CPU0 和 CPU1 上运行,并且它们共享一个存储缓冲区。在线程 1 执行 Store C = 1 后,它们的存储缓冲区会爆炸。
[A(wb), B, C]
因为thread2(在CPU2上运行)也使用smp_mb而不是barrier,所以它保证如果看到C == 1,A一定是1。
我在 MESI 内存一致性协议中描述了以上所有内容。也许作者的意思是有另一个协议使 thread1 中的屏障必须代替 smp_mb 来保证 cpu 的传递性?
谁能给我一个例子吗?
也许在特定协议中考虑传递性是一个错误。我们必须记住的是,rmb() 或 wmb() 并不能保证 cpu 的传递性,因为有很多不同的协议和架构。