【问题标题】:Simple C program to illustrate out of order execution?简单的 C 程序来说明乱序执行?
【发布时间】:2019-03-12 11:48:02
【问题描述】:

我正在运行 x86,我想实际看到一个由我的机器上的无序执行引起的错误。我试过写一个,based off this wiki article,但我总是看到“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”?

【问题讨论】:

  • 线程启动不够快。只需在主函数中创建sleep() 后添加它即可获得您的 11 值
  • 另一种方法是(也许)用一些 CPU 密集型进程加载系统以改变竞争条件
  • @Jean-FrançoisFabre 但handlerf = 1; 完成之前什么都不做,此时x = 33; 已经完成。
  • @Jean-FrançoisFabre 不要认为这里有竞争条件。
  • @kiranBiradar 在放弃 while (f == 0); 约束后,如果您多次运行该程序,也许迟早时间片调度会将 pthread_create(&amp;thread1, NULL, handler, NULL);x = 33; 分开并在其中运行 handler破解,举报11

标签: c multithreading x86 memory-barriers


【解决方案1】:

您尝试创建的效果不依赖于无序执行执行。这只是可以创建内存重新排序的事情之一。另外,现代 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 允许编译器将负载提升到循环之外,但必须始终完成 volatile 负载。https://electronics.stackexchange.com/questions/387181/mcu-programming-c-o2-optimization-breaks-while-loop#387478)。

生成的 asm / 机器代码中的存储将以 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);
    ...
}

对于负载端也是如此。 while(f==0){} 是 x86 上的获取加载,因此让读取端等待直到它看到非零 f 保证它也看到 x==33

但是,如果您为 ARM 或 PowerPC 之类的弱排序 ISA 进行编译,则 asm 级别的内存排序保证确实允许 StoreStore 和 LoadLoad 重新排序,因此您的程序可能会打印 11 如果编译时没有优化。

另见https://preshing.com/20120930/weak-vs-strong-memory-models/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    相关资源
    最近更新 更多