内核版本:kernel 0.12
首先看一段代码,下面这段代码来自内核版本0.12的mm/swap.c
中:
// mm/swap.c
#define bitop(name,op) \static inline int name(char * addr,unsigned int nr) \
{ \int __res; \__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \:"=g" (__res) \:"r" (nr),"m" (*(addr)),"0" (0)); \return __res; \
}bitop(bit,"")
bitop(setbit,"s")
bitop(clrbit,"r")
这段代码通过宏定义了三个位操作函数,分别是 bit() 测试位,setbit() 置位,clrbit() 清除位。
将上述代码进行改造,对setbit()
封装后:
// main.c
#define bitop(name,op) \static inline int name(char * addr,unsigned int nr) \
{ \int __res; \__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \:"=g" (__res) \:"r" (nr),"m" (*(addr)),"0" (0)); \return __res; \
}bitop(setbit,"s")int do_setbit(char *addr, unsigned int nr)
{return setbit(addr, nr);
}
反汇编后:
执行gcc -c -o main.o main.c && objdump -s -d main.o
0000000000000000 <setbit>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 48 89 7d e8 mov %rdi,-0x18(%rbp)8: 89 75 e4 mov %esi,-0x1c(%rbp)b: 8b 55 e4 mov -0x1c(%rbp),%edxe: 48 8b 4d e8 mov -0x18(%rbp),%rcx12: b8 00 00 00 00 mov $0x0,%eax // (1) 清零eax17: b8 00 00 00 00 mov $0x0,%eax1c: 0f ab 11 bts %edx,(%rcx) // (2) bts 置位1f: 83 d0 00 adc $0x0,%eax // (3) adc: eax = eax + 0 + CF22: 89 45 fc mov %eax,-0x4(%rbp)25: 8b 45 fc mov -0x4(%rbp),%eax28: 5d pop %rbp29: c3 retq 000000000000002a <do_setbit>:2a: 55 push %rbp2b: 48 89 e5 mov %rsp,%rbp2e: 48 83 ec 10 sub $0x10,%rsp32: 48 89 7d f8 mov %rdi,-0x8(%rbp)36: 89 75 f4 mov %esi,-0xc(%rbp)39: 8b 55 f4 mov -0xc(%rbp),%edx3c: 48 8b 45 f8 mov -0x8(%rbp),%rax40: 89 d6 mov %edx,%esi42: 48 89 c7 mov %rax,%rdi45: e8 b6 ff ff ff callq 0 <setbit>4a: c9 leaveq 4b: c3 retq
bt: 表示 Bit Test,测试并用原值设置进位值
bts: 表示 Bit Test and Set,设置比特位(设为 1)并用原值设置进位值
btr: 表示 Bit Test and Reset,复位比特位(设为 0)并用原值设置进位值
可以看到在setbit()
中最重要的几步:
(1) 清零eax:"0" (0)
(2) bts 置位:"bt" op " %1,%2
(3) adc: eax = eax + 0 + 溢出标记CF:adcl $0,%0
c语言内联汇编语法含义:
__asm__("汇编语句":输出寄存器:输入寄存器:会被修改的寄存器)"=" 操作数在指令中是只写的 (输出操作数)"r" 通用寄存器, 也就是eax,ebx,ecx,edx,esi,edi中的一个"m" 内存变量"g" 通用寄存器, 或者内存变量"0-9" 表示用它限制的操作数与某个指定的操作数匹配,注意作为限定符字母的 %0-%9 与指令中的 "0"-"9" 的区别,前者代表操作数, 后者描述操作数.%0, %1, %2 分别代表: __res, nr, *addradcl $0,%0 表示: 将 __res(%0) 加上立即数 0($0) 后, 将结果放入 __res(%0) 中"0" (0) : 第一个"0"表示__res, 第二(0)表示常量0, 整个语句意思是将__res初始化为0, 相当于 __res = 0
参考:
bt/bts/btr 指令
AT&T中的bt汇编指令
GCC 内联汇编