内核中的gcc扩展

本文收集Linux内核中用到的gcc的C扩展。转载请注明出处http://blog.csdn.net/enjoysilence/article/details/8929729。

1. 表达式中的语句和声明(Statements and Declarations in Expressions)

用一对大括号{}把多条语句括起来,就构成了一条复合语句,再用一对圆排号()将这条复合语句括起就构成了一条复合语句表达式,这个表达式的值就是复合语句中最后一条子表达式的值,这与Shell脚本有些类似,例如下面这条语句将foo()的绝对值赋值给整型变量a:

int a = ({ int y = foo (); 
           int z; 
           if (y > 0) z = y; 
           else z = - y; 
           z; })
这个特性可以使宏定义更加安全,在标准C中,通常这样来定义一个求最大值的宏:

#define max(x,y) ((x) > (y) ? (x):(y))
这里的x和y被求值了两次,对有副作用(如i++)的操作数会产生错误的结果。但如果使用Gcc的复合表达式,就可以使宏定义变得安全:

#define max(x, y) ({                            \
        typeof(x) _max1 = (x);                  \
        typeof(y) _max2 = (y);                  \
        (void) (&_max1 == &_max2);              \
        _max1 > _max2 ? _max1 : _max2; })
这是一个泛型宏,其中的typeof就像typedef的逆运算一样,取出操作数的类型,详见下面关于typeof的说明。
(void) (&_max1 == &_max2); 
语句用来确认x和y的类型是否相同 ,若不相同Gcc会给出错误提示,详见另一篇Blog《关于(void)(&_min1 == &min2)》。

2. typeof

typeof的含义如同typedef的逆运算,即取出其操作数的类型。它的语法却如同sizeof,其操作数可以是表达式也可以是类型,如下例所示:

typeof (a[0](1)) /*操作数是表达式*/
typeof (char *)  /*操作数是类型*/
typeof关键字常用来构造泛型宏,例如上方的max宏定义。
下面是更多的例子:

typeof (*x) y;    /*声明一个类型为*x的变量y*/

typeof (*x) z[4]; /*声明一个长为4的数组z,数组元素为*x类型*/
     
typeof (typeof (char *)[4]) a; /*等价于char *a[4]*/

3. 分支预测提示

学过汇编的同学都知道,条件语句由jmp族指令来实现。为了防止程序执行时过多的跳转影响性能,通常将最可能的情况安排在紧邻jmp指令之后。Gcc有个内建函数__builtin_expect用来指示编译器可能的分支,它的声明如下

long __builtin_expect (long exp, long c)
这个函数的语义是期望exp==c,返回值是exp的值。如

if (__builtin_expect (x, 0))
      foo ();
这条语句提示编译器我们并不想调用foo(),因为我们期望x==0,编译器可以据此来优化代码。

Linux内核定义了两个宏来使用这一特性(见$KERNEL_SRC$/linux/include/linux/compiler.h):

#define likely(x)	__builtin_expect(!!(x), 1)
#define unlikely(x)	__builtin_expect(!!(x), 0)
这两个宏可以望文生义,下面是个例子:

x = 5;
if(likely(x, 1)) { /*提示编译器执行x++分支的可能性很大*/
     x++;
}

4. inline

将函数声明为inline是建议Gcc在处理对该函数的调用时产生更加快速的代码,Gcc可以将该函数的代码在其调用者内展开,从而避免了函数调用的开销。需要注意的是,inline只是建议而已,不能强制Gcc进行展开处理。inline通常与static关键字一起使用,其含义就是static与inline的组合,即(1)局部于编译单元,即函数只能在其定义所在的.c源文件中使用;(2)inline属性,即建议Gcc在调用处展开。通常在需要引用函数地址和不能展开(如递归函数)的场合,Gcc不做展开处理,而像普通函数一样为inline函数产生独立的汇编代码。

这里有一篇关于Gcc中inline关键字的详细介绍:http://xushouze2006.blog.163.com/blog/static/16230032200831710196672/。

6. __builtin_prefetch

这个Gcc内建函数的声明如下

void __builtin_prefetch (const void *addr, ...)
其作用是在访问数据前数据移到Cache中,以避免Cache未命中产生的延迟。可以在数据即将被访问前插入这个函数调用,若目标平台支持,Gcc就可以产生数据预取指令。

addr参数指明预取数据的内存地址。另外还有两个可选参数rwlocalityrw值为编译时常量0或1,0表明为写操作预取;1为默认值,表明为读操作预取。locality取值为0-3,0意味着数据没有时间局部性,可以在访问后即从Cache中移除,3表明数据的时间局部性很强,在访问后应该保存在所有的Cache当中,1和2则分别表明较低和中等的时间局部性,默认值是3。


未完待续……



参考资料:

1.Gcc官方关于C扩展的介绍http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/C-Extensions.html#C-Extensions

2.Linux 内核中的 GCC 特性http://www.ibm.com/developerworks/cn/linux/l-gcc-hacks/

3.Gcc官方关于内建扩展函数的介绍http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

4.Gcc中inline关键字http://xushouze2006.blog.163.com/blog/static/16230032200831710196672/


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值