linux内核中的常用技术:内存屏障及volatile

  1. /** 
  2.  * clear_bit - Clears a bit in memory 
  3.  * @nr: Bit to clear 
  4.  * @addr: Address to start counting from 
  5.  * 
  6.  * clear_bit() is atomic and may not be reordered.  However, it does 
  7.  * not contain a memory barrier, so if it is used for locking purposes, 
  8.  * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() 
  9.  * in order to ensure changes are visible on other processors. 
  10.  */  
  11. static inline void clear_bit(int nr, volatile void *addr)  
  12. {  
  13.     asm volatile(LOCK_PREFIX "btr %1,%0" : ADDR : "Ir" (nr));  
  14. }
  1. /* 
  2.  * clear_bit_unlock - Clears a bit in memory 
  3.  * @nr: Bit to clear 
  4.  * @addr: Address to start counting from 
  5.  * 
  6.  * clear_bit() is atomic and implies release semantics before the memory 
  7.  * operation. It can be used for an unlock. 
  8.  */  
  9. static inline void clear_bit_unlock(unsigned nr, volatile void *addr)  
  10. {  
  11.     barrier();  
  12.     clear_bit(nr, addr);  
  13. }  
第一个函数比较容易理解,第二个函数比第一个多了个barrier()函数,这就是大名鼎鼎的内存屏障技术,定义如下
#define barrier() __asm__ __volatile__("": : :"memory")
解释一下:__volatitle__是防止编译器移动该指令的位置或者把它优化掉。"memory",是提示编译器该指令对内存修改,防止使用某个寄存器中已经load 的内存的值。
memory 强制gcc 编译器假设RAM 所有内存单元均被汇编指令修改,这样cpu 中的registers 和cache 中已缓存的内存单元中的数据将作废。cpu 将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu 又将registers,cache 中的数据用于去优化指令,而避免去访问内存。

事实上,不止barrier,还有一个mb系列的函数也起着内存屏障的功能:
  1. #include <asm/system.h>  
  2. "void rmb(void);"  
  3. "void wmb(void);"  
  4. "void mb(void);"  

这些函数在已编译的指令流中插入硬件内存屏障,具体的插入方法是平台相关的。rmb(读内存屏障)保证了屏障之前的读操作一定会在后来的读操作执行之前完成。wmb保证写操作不会乱序,mb 指令保证了两者都不会。这些函数都是 barrier 函数的超集。解释一下:编译器或现在的处理器常会自作聪明地对指令序列进行一些处理,比如数据缓存,读写指令乱序执行等等。如果优化对象是普通内存,那么一般会提升性能而且不会产生逻辑错误。但如果对I/O 操作进行类似优化很可能造成致命错误。所以要使用内存屏障,以强制该语句前后的指令以正确的次序完成。

其实在指令序列中放一个wmb 的效果是使得指令执行到该处时,把所有缓存的数据写到该写的地方,同时使得wmb 前面的写指令一定会在wmb后面 的写指令之前执行。

回到上面的函数,当clear_bit函数不用于实现锁的目的时,不用给它加上内存屏障(我的理解:不管是不是读到最新的数据,这一位就是要清零,不管加不加内存屏障,结果都是一样的);而当用于实现锁的目的时,必须使用clear_bit_unlock函数,其实现中使用了内存屏障,以此来确保此处的修改能在其他CPU上看到(我的理解:加锁操作就是为了在多个CPU间进行同步的目的,所以要避免寄存器优化,其他CPU每次都读内存这样才能看到最新的变化,这块不是太明白)。这种操作也叫做serialization,即在执行这条指令前,CPU必须要完成前面所有对memory的访问指令(read and write),这样是为了避免编译器进行某些优化


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值