4、Linux源代码中的关键字解释汇集

1、asmlinkage

     函数定义前加宏asmlinkage ,表示这些函数通过堆栈而不是通过寄存器传递参数。 gcc编译器在汇编过程中调用c语言函数时传递参数有两种方法:一种是通过堆栈,另一种是通过寄存器。缺省时采用寄存器,假如你要在你的汇编过程中调用c语言函数,并且想通过堆栈传递参数,你定义的c函数时要在函数前加上宏asmlinkage。

定义:在/usr/include/asm/linkage.h里有定义:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

#define FASTCALL(x) x __attribute__((regparm(3)))

#define fastcall __attribute__((regparm(3))) 

#define asmlinkage __attribute__((regparm(0))) 

(1)、__attribute__是关键字,是gcc的C语言扩展。

(2)、regparm(0)表示不从寄存器传递参数。

(3)、GCC中可以使用__attribute__((regparm(n)))指定最多可以使用n个寄存器(eax, edx, ecx)传递参数,n的范围是0~3,超过n时则将参数压入栈中(n=0表示不用寄存器传递参数),(n>0)这样可以减少一些入栈出栈操作,因此调用比较快。

2、attribute

(1)、对于关键字__attribute__,在标准的C语言中是没有的。它是Gcc中对C语言的一个扩展用法。关键字__attribute__可以用来设置一个函数或数据结构定义的属性。对一个函数设置属性的主要目的是使编译器对函数进行可能的优化。对函数设置属性,是在函数原型定义中设置,如下面一个例子:

void fatal_error() __attribute__ ((noreturn));
. . .
void fatal_error(char *message)
{
     fprintf(stderr,"FATAL ERROR: %s\n",message);
     exit(1);
}

        在这个例子中,noreturn属性告诉编译器,这个函数不返回给调用者,所以编译器就可以忽略所有与执行该函数返回值有关的代码。可以在同一个定义中,设置多个属性,各个属性用逗号分开即可。如下面的定义就是告诉编译器,它不改变全局变量和该函数不能扩展为内联函数。

(2)、int getlim() __attribute__ ((pure,noinline));

        属性(attributes)也可以用来设置变量和结构体的成员。如,为了保证结构体中的一个成员变量与结构体有特殊方式的对齐(alignment),可以用以下形式定义:

struct mong {

char id;

int code __attribute__ ((align(4)));

};

        address_space结构体中,显然__attribute__是用来设置结构体struct address_space的,就是给该结构体设置一个属性。设置什么样的属性呢?该结构体的属性是aligned(sizeof(long)) ,就是设置struct address_space结构体按sizeof(long)个字节对齐。

        这里的属性aligned的含义是:设置与内存地址对齐(alignment)的方式。如

        int alivalue __attribute__ ((aligned(32)));

        变量alivalue的地址就是32字节对齐。对于我们内核源码的例子,当然属性有很多中,不仅仅是aligned,比如还有deprecated、packed、unused等。并且设置变量或结构体的属性,与设置函数的属性有所不同。

(3)、00086:   __attribute__ ((unusedalias(__stringify(name))))

        前面已经提到,可以为一个变量或函数设置多个属性(attribute),各个属性之间用逗号隔开。该宏有两个属性:unused和alias。unused使该类型的数据项显示为未被使用的,这样编译时就不会产生任何告警信息;alias使该定义是其他符号的别名。如

void __f () { /* Do something. */; }

void f () __attribute__ ((weak, alias ("__f")));

定义“f”是“__f”的一个弱别名。

3、volatile

        关键字volatile标记变量可以改变,而没有告警信息。volatile告诉编译器每次访问时,该变量必须重新加载,而不是从拷贝或缓存中读取。需要使用volatile的场合有,当我们处理中断寄存器时,或者并发进程之间共享的变量。

__volatile__

       在嵌入式汇编代码中,经常看到__volatile__修饰符,我们提到__volatile__和volatile实际上是等同的,这里不多作强调。__volatile__修饰符对汇编代码非常重要。它告诉编译器不要优化内联的汇编代码。通常,编译器认为一些代码是冗余和浪费的,于是就试图尽可能优化这些汇编代码。

4、likely() 与 unlikely()       (此部分内容转载汇总而来)

在linux内核源码中,unlikely()和likely()是两个宏,它告诉编译器一个暗示。现代的CPU都有提前预测语句执行分支(branch-prediction heuristics)的功能,预测将要执行的指令,以优化执行速度。unlikely()和likely()通过编译器告诉CPU,某段代码是likely,应被预测;某段代码是unlikely,不应被预测。unlikely()和likely()定义在include/linux/compiler.h。

00106: # ifndef likely

00107: #  define likely(x(__builtin_constant_p(x! ! (x__branch_check__(x1))

00108: # endif

00109: # ifndef unlikely

00110: #  define unlikely(x)  (__builtin_constant_p(x! ! (x__branch_check__(x0))

00111: # endif

__builtin_expect是gcc中提供的一个预处理命令,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。。gcc(version 4.4.0)具体定义如下:
long __builtin_expect (long exp, long c) [Built-in Function]

__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。
也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,

if  (likely(prev  !=  next)) {
       next
-> timestamp  =  now;
        ...
else  {
        ...;
 }
通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

5、 __init、__initdata、__exit和__exitdata

        函数start_kernel()有个修饰符__init。__init实际上是一个宏,只有在linux内核初始化是执行的函数或变量前才使用__init。编译器将标记为__init的代码段存放在一个特别的内存区域里,这个区域在系统初始化后,就会释放。同理,__initdata用来标记只在内核初始化使用的数据,__exit和__exitdata用来标记结束或关机的例程。这些通常在设备驱动卸载时使用。

6、内核源码检查(转)

看进程管理内容时,do_fork()的源码是必读的。我们注意到do_fork()最后两个参数前,都有__user修饰符。那么这么修饰符的含义和用处是怎样的?摘自kernel/fork.c。

01397: lon do_fork(unsigned long clone_flags,

01398:             unsigned long  stack_start,

01399:             struct pt_regs *regs,

01400:             unsigned long  stack_size,

01401:             int __user *parent_tidptr,

01402:             int __user *child_tidptr)

01403: {

01404:       struct task_struct *p;

01405:       int trace 0;

01406:       long nr;

01407:

01408:       *

01409:        Do some preliminary argument and permissions checking before we

01410:        actually start allocating stuff

01411:        */

01412:       if (clone_flags CLONE_NEWUSER{

01413:               if (clone_flags CLONE_THREAD)

01414:                     return EINVAL;

01415:               * hopefully this check will go away when userns support is

01416:               complete

01417:               */

01418:               if (capable(CAP_SYS_ADMIN||  capable(CAP_SETUID||

01419:                            capable(CAP_SETGID))

01420:                     return EPERM;

01421:       }

 先来看__user的在include/linux/compiler.h中的定义:

00006: #ifdef     CHECKER     

00007: # define __user          __attribute__((noderefaddress_space(1)))

00008: # define __kernel * default address space */

00009: # define __safe           __attribute__((safe))

00010: # define __force  __attribute__((force))

00011: # define __nocast__attribute__((nocast))

00012: # define __iomem      __attribute__((noderefaddress_space(2)))

00013: # define __acquires(x)      __attribute__((context(x,0,1)))

00014: # define __releases(x)__attribute__((context(x,1,0)))

00015: # define __acquire(x__context__(x,1)

00016: # define __release(x__context__(x,1)

00017: # define __cond_lock(x,c((c(__acquire(x); 1}0)

00018: extern void __chk_user_ptr(const volatile void __user *);

00019: extern void __chk_io_ptr(const volatile void __iomem *);

00020: #else

00021: # define __user

00022: # define __kernel

00023: # define __safe

00024: # define __force

00025: # define __nocast

00026: # define __iomem

00027: # define __chk_user_ptr(x(void)0

00028: # define __chk_io_ptr(x(void)0

00029: # define __builtin_warning(x y...(1)

00030: # define __acquires(x)

00031: # define __releases(x)

00032: # define __acquire(x(void)0

00033: # define __release(x(void)0

00034: # define __cond_lock(x,c(c)

00035: #endif

        通过其定义,似乎Gcc中现在还没有支持这个用法。通过字面意思理解,__user很显然是告诉它是一个用户数据。虽然Gcc还不支持这种用法,但借助适当的工具,就可以在内核编译时就可以发现内核源码中的一些错误;如前面的__user,若编译时发现传递进来的不是用户数据,那么就产生告警。在__user定义中,我们发现还有__kernel、__safe、__force、__iomem,这些都是用来做内核源码语法检查的;其中__iomem在驱动代码中很常见。目前内核社区使用SPARSE工具来做内核源码的检查。SPARSE是语法分析器,能在编译器前端发现源码的语法。它能检查ANSI C以及很多Gcc的扩展。SPASE提供一系列标记来传递语法信息,如地址空间的类型、函数所需获取或释放的锁等。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风浅醉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值