barrier() 静态内存屏障,防止编译阶段的乱序与优化。
mb() 动态内存屏障,防止CPU处理的乱序。
barrier()在x86中的实现就是一行内嵌汇编代码:
测试代码如下:
对应汇编代码分别为:
分析结论:
1. 没有barrier()时,ga与gb的赋值顺序与C代码相反;
加入barrier()后,ga与gb的赋值顺序与C代码相同。
2. 没有barrier()时,return的值是直接通过寄存器计算出来的;
加入barrier()后,return时,ga的值需要重新从内存中读取。
虽然barrier()从汇编代码角度保证了ga与gb的赋值顺序,但是在支持乱序处理的CPU上,还是有可能打乱其顺序,因为CPU并不知道其依赖关系。这时,就需要使用mb()来保障顺序。
另一方面,使用barries()后[movl %edx, ga]和[addl ga, %eax]两条语句有明确的数据依赖关系,所以CPU绝不会打乱顺序执行。
综上述:
1. barries() 能保证同一内存地址的读写操作按序实实在在的进行,会刷新寄存器的缓存。
典型情况:[a++; barries(); b=a;]一定会读两次内存,而[a++; b=a;]则只会读一次。
mb() 动态内存屏障,防止CPU处理的乱序。
barrier()在x86中的实现就是一行内嵌汇编代码:
__asm volatile("": : :"memory");告诉编译器,barrier()前后的内存操作要确保按顺序实实在在的进行。测试代码如下:
int ga = 0;
int gb = 0;
#define barrier() __asm__ volatile("":::"memory")
int foo(int i)
{
ga = i*i;
barrier();
gb = i;
return ga+gb;
}对应汇编代码分别为:
#没有barrier()
foo:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
popl %ebp
movl %eax, %edx
imull %eax, %edx
movl %eax, gb
movl %edx, ga
leal (%edx,%eax), %eax
ret
#加入barrier()
foo:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl %eax, %edx
imull %eax, %edx
movl %edx, ga
movl %eax, gb
popl %ebp
addl ga, %eax
ret分析结论:
1. 没有barrier()时,ga与gb的赋值顺序与C代码相反;
加入barrier()后,ga与gb的赋值顺序与C代码相同。
2. 没有barrier()时,return的值是直接通过寄存器计算出来的;
加入barrier()后,return时,ga的值需要重新从内存中读取。
虽然barrier()从汇编代码角度保证了ga与gb的赋值顺序,但是在支持乱序处理的CPU上,还是有可能打乱其顺序,因为CPU并不知道其依赖关系。这时,就需要使用mb()来保障顺序。
另一方面,使用barries()后[movl %edx, ga]和[addl ga, %eax]两条语句有明确的数据依赖关系,所以CPU绝不会打乱顺序执行。
综上述:
1. barries() 能保证同一内存地址的读写操作按序实实在在的进行,会刷新寄存器的缓存。
典型情况:[a++; barries(); b=a;]一定会读两次内存,而[a++; b=a;]则只会读一次。
2. 要是两个不同内存操作的保序就得需要mb()来作保障。
本文探讨了在内存操作中,如何使用barrier()和mb()确保指令执行的顺序,特别是在汇编、编译器优化和多处理器环境下,这两个函数对于保持内存操作的正确次序至关重要。
2125

被折叠的 条评论
为什么被折叠?



