Init: int x = y = 0;
thr1 thr2
---- ----
y = 1; x = 1; WRITE to global variables
a = x; b = y; READ from global variables
print(a); print(b);
在TSO内存模型下运行的上述代码片段中,众所周知,两个线程可以同时打印0
,因为CPU可以重新排序指令来执行读指令(即线程的第二行)在写入指令之前。
那么,如果我们在读指令上插入断点,并且当断点被击中时什么也不做,只是继续执行,会发生什么呢?两个线程仍然可以同时打印 0
,或者断点会阻止重新排序吗?
我假设 x86-64 架构和 x86-TSO 内存模型。如果给出架构(或内存模型)之间的任何差异,我们将不胜感激。 另外,我猜硬件断点和软件断点不会对结果产生影响(因为它们都会触发异常,并且 x86 架构以类似的方式处理它们)。这个猜测正确吗?
最佳答案
我认为这是汇编的伪代码(不是可能发生大量编译时优化的 C 代码),与 https://preshing.com/20120515/memory-reordering-caught-in-the-act/ 的情况相同。其中 StoreLoad 重新排序将可见。
带有立即返回的断点处理程序的断点只是序列化指令(如 CPUID 或 IRET)的一个非常昂贵的替代方案,它确保所有先前的指令已被执行,并且所有先前的存储都被提交到一致的缓存(即耗尽存储缓冲区)。
这样做的原因是从断点返回可能会涉及 IRET ,这是一条序列化指令。 (类似于 MFENCE,但也序列化乱序执行)。
所以,是的,断点是其他线程观察到的内存重新排序的完全屏障。
这适用于硬件断点或软件断点,其中调试器必须恢复原始指令的第一个字节才能使其正常执行,而不是 0xcc int3
软件断点。
关于x86 - 断点在宽松内存模型上的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65679691/