内核版本: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 %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d e8 mov %rdi,-0x18(%rbp)
8: 89 75 e4 mov %esi,-0x1c(%rbp)
b: 8b 55 e4 mov -0x1c(%rbp),%edx
e: 48 8b 4d e8 mov -0x18(%rbp),%rcx
12: b8 00 00 00 00 mov $0x0,%eax // (1) 清零eax
17: b8 00 00 00 00 mov $0x0,%eax
1c: 0f ab 11 bts %edx,(%rcx) // (2) bts 置位
1f: 83 d0 00 adc $0x0,%eax // (3) adc: eax = eax + 0 + CF
22: 89 45 fc mov %eax,-0x4(%rbp)
25: 8b 45 fc mov -0x4(%rbp),%eax
28: 5d pop %rbp
29: c3 retq
000000000000002a <do_setbit>:
2a: 55 push %rbp
2b: 48 89 e5 mov %rsp,%rbp
2e: 48 83 ec 10 sub $0x10,%rsp
32: 48 89 7d f8 mov %rdi,-0x8(%rbp)
36: 89 75 f4 mov %esi,-0xc(%rbp)
39: 8b 55 f4 mov -0xc(%rbp),%edx
3c: 48 8b 45 f8 mov -0x8(%rbp),%rax
40: 89 d6 mov %edx,%esi
42: 48 89 c7 mov %rax,%rdi
45: 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, *addr
adcl $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 内联汇编