#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) 判断是否为 2 的幂
#include <asm/system.h>
"void rmb(void);"
"void wmb(void);"
"void mb(void);"
这些函数在已编译的指令流中插入硬件内存屏障;具体的插入方法是平台相关的。
rmb
(读内存屏障)保证了屏障之前的读操作一定会在后来的读操作执行之前完
成
。
wmb
保证写操作不会乱序,
mb
指令保证了两者都不会。这些函数都是
barrier
函数的超集。解释一下:编译器或现在的处理器常会自作聪明地
对指令序列进行一些处理,比如数据缓存,读写指令乱序执行等等。如果优化对象是普通内存,那么一般会提升性能而且不会产生逻辑错误。但如果对
I/O
操作进
行类似优化很可能造成致命错误。所以要使用内存屏障,以强制该语句前后的指令以正确的次序完成。其实在指令序列中放一个
wmb
的效果是使得指令执行到该处
时,把所有缓存的数据写到该写的地方,同时使得
wmb
前面的写指令一定会在
wmb
的写指令之前执行。
内存屏障
MB(memory barrier,
内存屏障
) :x86
采用
PC(
处理机
)
内存一致性模型,使用
MB
强加的严格的
CPU
内存事件次序,保证程序的执行
看上去象是遵循顺序一致性
(SC)
模型,当然,即使对于
UP
,由于内存和设备见仍有一致性问题,这些
Mb
也是必须的。在当前的实现中,
wmb()
实际上是
一个空操作,这是因为目前
Intel
的
CPU
系列都遵循
“
处理机一致性
”
,所有的写操作是遵循程序序的,不会越过前面的读写操作。但是,由于
Intel CPU
系列可能会在将来采用更弱的内存一致性模型并且其他体系结构可能采用其他放松的一致性模型,仍然在内核里必须适当地插入
wmb()
保证
内存事件的正确次序。
见头文件
include/asm/system.h
#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
#define rmb() mb()
#define wmb() __asm__ __volatile__ ("": : :"memory")
此外,
barrier
实际上也是内存屏障。
include/linux/kernel.h:
#define barrier() __asm__ __volatile__("": : :"memory")
内存屏障也是一种避免锁的技术。
它在进程上下文中将一个元素插入一个单向链表:
new->next=i->next;
wmb();
i->next=new;
同时,如果不加锁地遍历这个单向链表。或者在遍历链表时已经可以看到
new
,或者
new
还不在该链表中。
Alan Cox
书写这段代码时就注意到了这一
点,两个内存写事件的顺序必须按照程序顺序进行。否则可能
new
的
next
指针将指向一个无效地址,就很可能出现
OOPS!
不论是
gcc
编译器的优化还是处理器本身采用的大量优化,如
Write buffer, Lock-up free, Non-
blocking reading, Register allocation, Dynamic scheduling, Multiple issues
等,都可能使得实际执行可能违反程序序,因此,引入
wmb
内存屏障来保证两个写事件的执行次序严格按程序顺序来执行。