|
平台:X86 32位
内核:2.6.24
LOCK_PREFIX 宏在内核里随处可见,其主要用途是确保原子操作。它定义在 include/asm-x86/Alternative_32.h 中:#ifdef CONFIG_SMP
#define LOCK_PREFIX \
".section .smp_locks,\"a\"\n" \
" .align 4\n" \
" .long 661f\n" /* address */ \
".previous\n" \
"661:\n\tlock; "
#else /* ! CONFIG_SMP */
#define LOCK_PREFIX ""
#endif 该宏的内核内容是 LOCK 指令前缀,关于 LOCK 指令前缀可以参考:http://www.groad.net/bbs/read.php?tid-3152.html
从宏的定义中,可以看到该宏主要用于 SMP 环境中。
在一些原子操作函数里可以看到该宏的应用:static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
LOCK_PREFIX "addl %1,%0"
:"+m" (v->counter)
:"ir" (i));
}
下面将一些内核中的原子操作函数和该宏的定义都放在用户态里使用以能了解该宏的一些定义细节,程序代码如下:#include <stdio.h>
#define LOCK_PREFIX \
".section .smp_locks,\"a\"\n" \
" .align 4\n" \
" .long 661f\n" /* address */ \
".previous\n" \
"661:\n\tlock; "
typedef struct { int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
static __inline__ void atomic_inc(atomic_t *v)
{
__asm__ __volatile__(
LOCK_PREFIX "incl %0"
:"+m" (v->counter));
}
static __inline__ void atomic_dec(atomic_t *v)
{
__asm__ __volatile__(
LOCK_PREFIX "decl %0"
:"+m" (v->counter));
}
static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
LOCK_PREFIX "addl %1,%0"
:"+m" (v->counter)
:"ir" (i));
}
int main()
{
atomic_t count = ATOMIC_INIT(0);
atomic_inc (&count);
atomic_dec (&count);
atomic_add (1, &count);
return (0);
} 为了简便起见,只将程序编译生成为 .o 目标文件,而不生成可执行文件:$ gcc -c lock_prefix.c -o lock_prefix.o
使用 objdump 查看:beyes@linux-kd1q:~/assembly> objdump -h lock_prefix.o
lock_prefix.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000006a 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 000000a0 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 000000a0 2**2
ALLOC
3 .smp_locks 0000000c 00000000 00000000 000000a0 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
4 .comment 00000043 00000000 00000000 000000ac 2**0
CONTENTS, READONLY
5 .comment.SUSE.OPTs 00000006 00000000 00000000 000000ef 2**0
CONTENTS, READONLY
6 .note.GNU-stack 00000000 00000000 00000000 000000f5 2**0
CONTENTS, READONLY 从上面可以看到,.smp_locks 段一共有 0xc 即 12 个字节的数据。下面的分析可以观察到这些数据是什么:beyes@linux-kd1q:~/assembly> objdump -D lock_prefix.o
lock_prefix.o: file format elf32-i386
Disassembly of section .text:
00000000 <atomic_inc>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 8b 55 08 mov 0x8(%ebp),%edx
9: f0 ff 00 lock incl (%eax)
c: 5d pop %ebp
d: c3 ret
0000000e <atomic_dec>:
e: 55 push %ebp
f: 89 e5 mov %esp,%ebp
11: 8b 45 08 mov 0x8(%ebp),%eax
14: 8b 55 08 mov 0x8(%ebp),%edx
17: f0 ff 08 lock decl (%eax)
1a: 5d pop %ebp
1b: c3 ret
0000001c <atomic_add>:
1c: 55 push %ebp
1d: 89 e5 mov %esp,%ebp
1f: 8b 45 0c mov 0xc(%ebp),%eax
22: 8b 55 08 mov 0x8(%ebp),%edx
25: 8b 4d 0c mov 0xc(%ebp),%ecx
28: f0 01 10 lock add %edx,(%eax)
2b: 5d pop %ebp
2c: c3 ret
0000002d <main>:
2d: 55 push %ebp
2e: 89 e5 mov %esp,%ebp
30: 83 ec 18 sub $0x18,%esp
33: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
3a: 8d 45 fc lea -0x4(%ebp),%eax
3d: 89 04 24 mov %eax,(%esp)
40: e8 bb ff ff ff call 0 <atomic_inc>
45: 8d 45 fc lea -0x4(%ebp),%eax
48: 89 04 24 mov %eax,(%esp)
4b: e8 be ff ff ff call e <atomic_dec>
50: 8d 45 fc lea -0x4(%ebp),%eax
53: 89 44 24 04 mov %eax,0x4(%esp)
57: c7 04 24 01 00 00 00 movl $0x1,(%esp)
5e: e8 b9 ff ff ff call 1c <atomic_add>
63: b8 00 00 00 00 mov $0x0,%eax
68: c9 leave
69: c3 ret
Disassembly of section .smp_locks:
00000000 <.smp_locks>:
0: 09 00 or %eax,(%eax)
2: 00 00 add %al,(%eax)
4: 17 pop %ss
5: 00 00 add %al,(%eax)
7: 00 28 add %ch,(%eax)
9: 00 00 add %al,(%eax)
...
Disassembly of section .comment:
00000000 <.comment>:
0: 00 47 43 add %al,0x43(%edi)
3: 43 inc %ebx
4: 3a 20 cmp (%eax),%ah
6: 28 53 55 sub %dl,0x55(%ebx)
9: 53 push %ebx
a: 45 inc %ebp
b: 20 4c 69 6e and %cl,0x6e(%ecx,%ebp,2)
f: 75 78 jne 89 <main+0x5c>
11: 29 20 sub %esp,(%eax)
13: 34 2e xor $0x2e,%al
15: 35 2e 30 20 32 xor $0x3220302e,%eax
1a: 30 31 xor %dh,(%ecx)
1c: 30 30 xor %dh,(%eax)
1e: 36 30 34 20 xor %dh,%ss:(%eax,%eiz,1)
22: 5b pop %ebx
23: 67 63 63 2d arpl %sp,0x2d(%bp,%di)
27: 34 5f xor $0x5f,%al
29: 35 2d 62 72 61 xor $0x6172622d,%eax
2e: 6e outsb %ds:(%esi),(%dx)
2f: 63 68 20 arpl %bp,0x20(%eax)
32: 72 65 jb 99 <main+0x6c>
34: 76 69 jbe 9f <main+0x72>
36: 73 69 jae a1 <main+0x74>
38: 6f outsl %ds:(%esi),(%dx)
39: 6e outsb %ds:(%esi),(%dx)
3a: 20 31 and %dh,(%ecx)
3c: 36 30 32 xor %dh,%ss:(%edx)
3f: 39 32 cmp %esi,(%edx)
41: 5d pop %ebp
...
Disassembly of section .comment.SUSE.OPTs:
00000000 <.comment.SUSE.OPTs>:
0: 6f outsl %ds:(%esi),(%dx)
1: 73 70 jae 73 <main+0x46>
3: 77 67 ja 6c <main+0x3f>
... 现在再回头来看 LOCK_PREFIX 的语句:
.section .smp_locks,\"a\"\n" \ :该条语句定义了一个名为 .smp_lock 的段,后接 "a" 属性表示 "allocatable" 。
.align 4 :该段 4 字节对其
.long 661f :.long 伪指令用来生成一个 long 型整数;现在其后接一个标号 661,那么这个整数的值则为这个标号所在处的地址(在后面看到,这个标号处存放的就是具有 lock 指令前缀的指令,所以具有 lock 指令前缀的指令地址也就是此处的值)。
.previous :后面的代码切换回到 .text 段,并将它下面的 lock 指令前缀搬移到 .text 段中。关于 .previous 伪指令的细节可参考:http://www.groad.net/bbs/read.php?tid-3150.html
从上面 objdump 反汇编所得到的代码中,蓝色部分即为 .smp_lock 段的内容,仔细观察,这些内容分别为粉红色标识的具有 lock 指令前缀的指令地址。所以,.smp_lock 段实质上就是这些指令的地址的数组。 |
|