在Linux内核中,提供了比较交换的函数cmpxchg,代码在include/asm-i386/cmpxchg.h中,函数的原型是:
[code]
cmpxchg ( void *ptr, unsigned long old, unsigned long new);
[/code]
函数作用:将old和ptr指向内容作比较,如果相等那么将new写入ptr中,返回old;否则,不行等就返回ptr的内容。EAX是提供返回使用的。
[code]
#define cmpxchg(ptr,o,n)\
((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
(unsigned long)(n),sizeof(*(ptr))))[/code]
[code]
static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
unsigned long new, int size)
{
unsigned long prev;
switch (size) {
case 1:
__asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
: "=a"(prev)
: "q"(new), "m"(*__xg(ptr)), "0"(old)
: "memory");
return prev;
case 2:
__asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
: "=a"(prev)
: "r"(new), "m"(*__xg(ptr)), "0"(old)
: "memory");
return prev;
case 4:
__asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
: "=a"(prev)
: "r"(new), "m"(*__xg(ptr)), "0"(old)
: "memory");
return prev;
}
return old;
}
[/code]
以2字节交换为例,主要的操作就是汇编指令cmpxchgw %1,%2,注意一下其中的%2,也就是后面的"m"(*__xg(ptr))。
__xg是在这个文件中定义的宏:
struct __xchg_dummy { unsigned long a[100]; };
#define __xg(x) ((struct __xchg_dummy *)(x))
那么%2经过预处理,展开就是"m"(*((struct __xchg_dummy *)(ptr))),这种做法,就可以达到在cmpxchg中的%2是一个地址,就是ptr指向的地址。如果%2是"m"(ptr),那么指针本身的值就出现在cmpxchg指令中。
奔腾指令:
Opcode Instruction 64-Bit Mode Compat/ Description
Leg Mode
0F B1/r CMPXCHG r/m16, r16 Valid Valid* Compare AX with r/m16. If
equal, ZF is set and r16 is
loaded into r/m16. Else,
clear ZF and load r/m16
into AX.
cmpxchg bx, cx:
如果AX与BX相等,则CX送BX且ZF置1;否则BX送AX,且ZF清0
换成AT&T的格式就是
由于ATT汇编与Intel不同,操作数2,和操作数1互换。
cmpxchg %cx, %bx;
送入AX,再看AX就是prev。