Linux内核源码阅读系列(1)-可能令人迷惑的C语言语法
Linux内核代码中使用了很多自定义的宏,如果初次开始读代码可能会感到迷惑。比如下面这个:
if (unlikely(!mm)) {
next->active_mm = oldmm;
atomic_inc(&oldmm->mm_count);
enter_lazy_tlb(oldmm, next);
} else
switch_mm(oldmm, mm, next);
unlikely?首先这个不是C语言的关键字,那就是宏定义了。事实上,内核代码中将likely和unlikely定义为两个帮助编译器优化的宏。宏定义如下:
#define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0)
__builtin_expect()宏是gcc(version>=2.9)引入的预先定义的宏,这个宏的主要作用是帮助编译器判断条件跳转的预期值。看看下面这个例子:
if (__builtin_expect (x, 0))
foo ();
这里例子中,代码是在说预期x的值为“假”,并且不太期望程序执行foo()函数。而这段代码就等价于:
if (unlikely(x))
foo ();
那么这个宏定义是如何影响编译器的行为的呢?看下面的一个例子(来自http://kerneltrap.org/node/4705):
[kedar@ashwamedha ~]$ cat abc.c
int
testfun(int x)
{
if(__builtin_expect(x, 0)) {
^^^--- 在这里我们告诉编译器, "else" 块的代码是我们比较期待的结果
x = 5;
x = x * x;
} else {
x = 6;
}
return x;
}
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump -d abc.o
abc.o: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 75 07 jne 11 < testfun+0x11 >
^^^ --- 编译器在这里为"if"块产生跳转代码但是却保持"else"块顺序执行
a: b8 06 00 00 00 mov $0x6,%eax
f: c9 leave
10: c3 ret
11: b8 19 00 00 00 mov $0x19,%eax
16: eb f7 jmp f < testfun+0xf >
如果将源代码中的unlikely改为likely,编译器生成的代码将会于上面的情况恰恰相反:
[kedar@ashwamedha ~]$ cat abc.c
int
testfun(int x)
{
if(__builtin_expect(x, 1)) {
^^^ --- 在这里我们告诉编译器, "if" 块的代码是我们比较期待的结果
x = 5;
x = x * x;
} else {
x = 6;
}
return x;
}
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump -d abc.o
abc.o: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 74 07 je 11 < testfun+0x11 >
^^^ --- 编译器在这里为"else"块产生跳转代码但是却保持"if"块顺序执行
a: b8 19 00 00 00 mov $0x19,%eax
f: c9 leave
10: c3 ret
11: b8 06 00 00 00 mov $0x6,%eax
16: eb f7 jmp f < testfun+0xf >
由此可见likely和unlikely仅仅是在帮助编译器产生更优代码,而对真值的判断没有影响。读代码的时候记住这一点就可以了。
相关日志
嘿~,如果您喜欢我的博客,您可以通过RSS.链接将本博客的最新文章传输到您喜欢的阅读器。

一月 12th, 2009 at 17:56
在内核中是这样定义的 include/linux/compiler.h :
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
参考: http://kernelnewbies.org/FAQ/LikelyUnlikely
一月 13th, 2009 at 15:06
Thanks, Border.
内核代码中对判断条件两次取非,这将保证这个x的值能够顺利的和1或者0比较。
四月 5th, 2010 at 17:03
“likely和unlikely仅仅是在帮助编译器产生更优代码,而对真值的判断没有影响”
Right!