我正在运行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?
如果xf最终出现在不同的缓存线中,则顺序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/

07-24 09:46
查看更多