分类: LINUX
2010-05-12 13:31:56
|
|
看上去如果z不计算出来是无法继续的。但是实际上CPU有可能先把三个加法都同时进行计算,然后根据z=x+y的结果直接挑选正确的p值。
因此,即使是从汇编上看顺序正确的指令,其执行的顺序也是不可预知的。处理器能够保证并发和乱序执行不会得到错误结果,但是如果是对一些硬件寄存器的操作不能允许乱序的话,程序员就必须把这个情况告诉CPU。告诉的方法就是通过CPU提供的一组同步指令实现,通常在CPU的文档里面有对同步指令的使用说明。系统函数库里面的内存屏障(rmb/wmb/mb)实际上也是通过这些同步指令实现的。因此在C编码的时候,只要设置好内存屏障,就能告诉CPU 哪些代码是不能乱序的。
对于切实是需要保障访存顺序的代码,就算当前使用的编译器能够编译出有序的目标码来,我们也还是必须通过设置内存屏障的方式来保证有序,否则都是不严谨,有隐患的。
Barrier屏障函数
Barrier函数可以在代码中设置屏障,这个屏障可以阻挡编译器的优化,也可以阻挡处理器的优化。
对于编译器来说,设置任何一个屏障都可以保证:
1. 编译器的乱序优化不会跨越屏障,即屏障前后的代码不会乱序;
2. 在屏障后所有对变量或者地址的操作,都会重新从内存中取值(相当于刷新寄存器中的变量副本)。
而对于处理器来说,根据不同的屏障有不同的表现(以下仅仅列举3种最简单的屏障):
1. 读屏障rmb()
处理器对读屏障前后的取数指令(LOAD)能保证有序,但是不一定能保证其他算术指令或者是写指令的有序。对于读指令的执行完成时间也不能保证,即它不能保证在屏障之前的读指令一定都执行完成,只能保证屏障之前的读指令一定能在屏障之后的读指令之前完成。
2. 写屏障wmb()
处理器对屏障前后的写指令(STORE)能保证有序,但是不一定能保证其他算术指令或者是读指令的有序。对于写指令的执行完成时间也不能保证,即它不能保证在屏障之前的写指令一定都执行完成,只能保证屏障之前的写指令一定能在屏障之后的写指令之前完成。
3. 通用内存屏障mb()
处理器保障只有屏障之前的访存操作(包括读写)都完成以后才会执行屏障之后的访存操作。即可以保障读写之间的有序(但是同样无法保证指令完成的时间)。这种屏障对处理器的执行单元效率产生的负面影响要比单纯用读屏障或者写屏障来的大。比如对于PowerPC来说这种通用屏障通常是使用sync指令实现的,在这种情况下处理器会丢弃所有预取的指令并清空流水线。所以频繁使用内存屏障会降低处理器执行单元的效率。
对于驱动开发者来说,一些对设备寄存器的操作,通常是必须保证有序的。在绝大部分情况下,一般都是写操作。对于有序的写操作,必须设置写屏障(wmb):
例:在驱动中使用写屏障 :
|