内存屏障: barrier()与mb()

本文探讨了在内存操作中,如何使用barrier()和mb()确保指令执行的顺序,特别是在汇编、编译器优化和多处理器环境下,这两个函数对于保持内存操作的正确次序至关重要。
barrier() 静态内存屏障,防止编译阶段的乱序与优化。
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()来作保障。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值