2011年(2)
分类: Java
2011-12-13 22:34:42
本文来自:
欢迎大家关注:
1. 前提基础关于上面这两点,可以参照和Intel的ia-32-architectures第3A卷的第8章。
2. 一个事例和一个推论和一个假设一个例子:有两个volatile变量x, y,初始值均为0,有两个处理器1和2分别对x, y做读写操作,操作顺序如下:
处理器1 : Store x 1 | Store y 1
处理器2 : Load y | Load x
因为对于处理器1,Store x 1和Store y 1不能重排,所以一定是Store x 1先;
同理,对于处理器2,Load y和Load x不能重排,所以一定是Load y先;
将四条操作针对x, y给出可能的操作顺序如下:
那么就有这样一个推论:
当Load y发现y == 1时,则Load x的值一定也是1, 当x == 0时,y也一定是0.
因为y == 1,则说明Store y 1被执行,则Store x 1肯定也会被执行, 而当x == 0则,表示Load x 在 Store x 1之前,则Load y
也一定在Store y 1之前。
只针对x, 假设对y volatile变量的两步操作为原子操作的话,那么情况就有变了,只剩下这两种情况:
这个时候,就能保证Store x 1之后再Load x了
3. 一个巧妙的处理就有了现在Java版的volatile有什么方法可以实现y的原子操作呢?
这个操作肯定是针对所有处理器而言的操作, 那么很容易想到锁总线的操作,如Lock前缀,
Lock前缀只适用于特定的一系列的指令,对于不适用的会抛出异常,具体大家很容就能找到,
曾经有人还利用这一点,做出很多巧妙的事情来,通过这个无用操作抛出异常,
可以hack到当时的内核地址信息等。
有点扯远了,在openJDK7的orderAccess_linux_x86.inline.hpp中,实现了这样的一个内联函数:
inline void OrderAccess::fence() {
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : :
: "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : :
: "cc", "memory");
#endif
}
}
顾名思义,该内联函数类似与barrier的效果,及内存屏障,看
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
可以得到这样一些信息:
根据第3点得到的信息,其实可以这样理解,当两个处理器针对一个volatile做操作时,一个在Store而另一个在Load时,
因为fence()的lock使得Store和Load不得不错开,而Load自然会推迟到Store完成之后,所以才有了这个Happen-Before原则:
volatile变量的写先于volatile变量的读.