往期链接:
linux设备驱动中的并发
本期主题:
编译乱序和执行乱序
目录
1.从实际例子引出编译乱序
1.为什么会有编译乱序?2.编译屏障barrier()设置
1.从实际例子引出编译乱序 1.为什么会有编译乱序?
编译器的工作就是帮我们优化代码性能,在不改变程序行为的情况下重新排序一些指令。但是有一个关键问题在于,编译器并不知道什么代码需要关注线程安全,它只会按照单线程的方式来处理这些指令,所以这样就存在了编译乱序的问题,因此,我们如果清楚知道哪些代码不应该被优化,我们就需要明确的告诉编译器。
看实际例子:
int a, b;
#define barrier() __asm__ __volatile__("": : :"memory")
void test_func(void)
{
a = b + 1;
b = 0;
}
int main(void)
{
test_func();
return 0;
}
gcc默认为O0编译,我们先看下O0编译完之后的目标文件反汇编的结果:
jason@ubuntu:~/WorkSpace/2.Linux_Driver/1.compile$ arm-none-linux-gnueabi-gcc -O0 main.c jason@ubuntu:~/WorkSpace/2.Linux_Driver/1.compile$ arm-none-linux-gnueabi-objdump -d a.out > O0.asm
O0反汇编的结果表明与代码是一致的,没有乱序
用同样的方式测试O2优化过后的结果:
发现经过O2优化的代码,反汇编出来的指令执行顺序有改变,这是编译器的优化结果。
可以使用编译屏障barrier()来保证上述指令不会被优化。
__asm__ 表示这是内嵌汇编,内嵌汇编的语法如下:__asm__ (汇编语句模板: 输出部分: 输入部分: 描述部分),其中memory描述表示直接从内存取,而不要用原来寄存器中的缓存;有一篇文章关于内嵌汇编讲得比较好 gcc内嵌汇编简单介绍需要注意的是,C语言内的volatile关键字和这个意义并不完全相同,C语言的volatile主要是避免了内存访问的合并行为,告诉了编译器,这个变量是有可能被外部修改的,因此需要重新读一下,不能用寄存器中的缓存值;
实验验证,添加了barrier()之后,O2编译的顺序也是正确的



