我正在运行x86,我想看到一个由我的机器上的无序执行引起的bug。我试着写一个,但是我总是看到“x的值是33”:
#include<stdio.h>
#include<pthread.h>
#include <sys/types.h>
int x, f;
void *handler(void *ptr) {
while (f == 0);
// Expectation: Sometimes, this should print 11 due to out-of-order exec
printf("value of x is %d \n", x);
return NULL;
}
int main() {
pthread_t thread1;
while(1) {
x = 11; f = 0;
pthread_create(&thread1, NULL, handler, NULL);
x = 33;
f = 1;
pthread_join(thread1, NULL);
}
return 0;
}
什么是最简单的C程序,可以说明一个无序的执行错误?为什么有时不打印“x的值是11”?
最佳答案
您试图创建的效果不依赖于无序执行。这只是造成内存重新排序的原因之一。另外,现代x86的执行是无序的,但它使用其内存顺序缓冲区来确保存储提交到l1d/以程序顺序变得全局可见。(因为x86的内存模型只允许storeload重新排序,而不允许storestore。)
内存重新排序与指令执行重新排序是分开的,因为即使按照顺序,CPU也会使用存储缓冲区来避免缓存未命中存储时暂停。
Out-of-order instruction execution: is commit order preserved?
Are loads and stores the only instructions that gets reordered?
如果x
和f
最终出现在不同的缓存线中,则顺序arm cpu上的c实现可以打印11或33。
我假设您在编译时禁用了优化,所以编译器会有效地处理所有变量volatile
,即volatile int x,f
。否则while(f==0);
循环将编译为if(f==0) { infloop; }
,只检查一次f
。(非原子变量的数据竞争ub允许编译器从循环中提升负载,但必须始终完成负载。https://electronics.stackexchange.com/questions/387181/mcu-programming-c-o2-optimization-breaks-while-loop#387478)。
生成的asm/machine代码中的存储将按c源代码顺序显示。
您正在为x86编译,它有一个强大的内存模型:x86存储是释放存储,x86加载是获取加载。你不能得到连续一致性,但你可以免费得到acq_rel。(对于未优化的代码,即使不要求也会发生这种情况。)
因此,在没有针对x86进行优化的情况下编译时,程序相当于
_Atomic int x, f;
int main(){
...
pthread_create
atomic_store_explicit(&x, 33, memory_order_release);
atomic_store_explicit(&f, 1, memory_order_release);
...
}
负载侧也是如此。
volatile
是x86上的一个获取负载,因此让读端等待它看到非零的while(f==0){}
保证它也看到f
。但是,如果您为弱顺序的isa(如arm或powerpc)编译,asm级别的内存顺序保证了它确实允许store和loadload重新排序,因此如果编译时没有优化,您的程序可能会打印
x==33
。另见https://preshing.com/20120930/weak-vs-strong-memory-models/