- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在运行 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”?
最佳答案
您尝试创建的效果不依赖于乱序执行。这只是可以创建内存重新排序的事情之一。此外,现代 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 允许编译器将负载提升到循环之外,但 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/
关于c - 简单的C程序来说明乱序执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52691544/
我是一名优秀的程序员,十分优秀!