设为首页 加入收藏

TOP

2.4.2 GNU C
2013-10-12 06:58:50 来源: 作者: 【 】 浏览:99
Tags:2.4.2 GNU

2.4.2 GNU C

像所有自视清高的Unix内核一样,Linux内核是用C语言编写的。让人略感惊讶的是,内核并不完全符合ANSI C标准。实际上,只要有可能,内核开发者总是要用到gcc提供的许多语言的扩展部分。(gcc是多种GNU编译器的集合,它包含的C编译器既可以编译内核,也可以编译Linux系统上用C语言写的其他代码。)

内核开发者使用的C语言涵盖了ISO C99标准和GNU C扩展特性。这其中的种种变化把Linux内核推向了gcc的怀抱,尽管目前出现了一些新的编译器如Intel C,已经支持了足够多的gcc扩展特性,完全可以用来编译Linux内核了。最早支持gcc的版本是 3.2,但是推荐使用gcc 4.4或之后的版本。Linux内核用到的ISO C99标准的扩展没有什么特别之处,而且C99作为C语言官方标准的修订本,不可能有大的或是激进的变化。让人感兴趣的,与标准C语言有区别的,通常也是人们不熟悉的那些变化,多数集中在GNU C上。就让我们研究一下内核代码中所使用到的C语言扩展中让人感兴趣的那部分吧,这些变化使内核代码有别于你所熟悉的其他项目。

1. 内联(inline)函数

C99和GNU C均支持内联函数。inline这个名称就可以反映出它的工作方式,函数会在它所调用的位置上展开。这么做可以消除函数调用和返回所带来的开销(寄存器存储和恢复)。而且,由于编译器会把调用函数的代码和函数本身放在一起进行优化,所以也有进一步优化代码的可能。不过,这么做是有代价的(天下没有免费的午餐),代码会变长,这也就意味着占用更多的内存空间或者占用更多的指令缓存。内核开发者通常把那些对时间要求比较高,而本身长度又比较短的函数定义成内联函数。如果一个函数较大,会被反复调用,且没有特别的时间上的限制,我们并不赞成把它做成内联函数。

定义一个内联函数的时候,需要使用static作为关键字,并且用inline限定它。比如:

  1. static inline void wolf(unsigned long tail_size) 

内联函数必须在使用之前就定义好,否则编译器就没法把这个函数展开。实践中一般在头文件中定义内联函数。由于使用了static作为关键字进行限制,所以编译时不会为内联函数单独建立一个函数体。如果一个内联函数仅仅在某个源文件中使用,那么也可以把它定义在该文件开始的地方。

在内核中,为了类型安全和易读性,优先使用内联函数而不是复杂的宏。

2. 内联汇编

gcc编译器支持在C函数中嵌入汇编指令。当然,在内核编程的时候,只有知道对应的体系结构,才能使用这个功能。

我们通常使用asm()指令嵌入汇编代码。例如,下面这条内联汇编指令用于执行x86处理器的rdtsc指令,返回时间戳(tsc)寄存器的值:

  1. unsigned int low, high;  
  2. asm volatile("rdtsc" : "=a" (low), "=d" (high));  
  3. /* low和high分别包含64位时间戳的低32位和高32位 */ 

Linux的内核混合使用了C语言和汇编语言。在偏近体系结构的底层或对执行时间要求严格的地方,一般使用的是汇编语言。而内核其他部分的大部分代码是用C语言编写的。

3. 分支声明

对于条件选择语句,gcc内建了一条指令用于优化,在一个条件经常出现,或者该条件很少出现的时候,编译器可以根据这条指令对条件分支选择进行优化。内核把这条指令封装成了宏,比如likely()和unlikely(),这样使用起来比较方便。

例如,下面是一个条件选择语句:

  1. if (error) {  
  2.        /* ... */  

如果想要把这个选择标记成绝少发生的分支:

  1. /* 我们认为error绝大多数时间都会为0...*/  
  2. if (unlikely(error)) {  
  3.        /* ... */  
  4.  

相反,如果我们想把一个分支标记为通常为真的选择:

  1. /* 我们认为success通常都不会为0 */  
  2. if  (likely(success)) {  
  3.       /* ... */  
  4.  

在你想要对某个条件选择语句进行优化之前,一定要搞清楚其中是不是存在这么一个条件,在绝大多数情况下都会成立。这点十分重要:如果你的判断正确,确实是这个条件占压倒性的地位,那么性能会得到提升;如果你搞错了,性能反而会下降。正如上面这些例子所示,通常在对一些错误条件进行判断的时候会用到unlikely()和likely()。你可以猜到,unlikely()在内核中会得到更广泛的使用,因为if语句往往判断一种特殊情况。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇22.8 函数类型和函数指针类型 下一篇1.2.4 C编程语言

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: