GCC 的一大特色就是__attribute__机制。

__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

attribute前后都有两个下划线,后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数。

语法格式为:__attribute__ ((attribute-list))

其位置约束为:放于声明尾部“;”之前。

函数属性(Function Attribute)

函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大

__attribute__ format

该__attribute__属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。

format的语法格式为:

format (archetype, string-index, first-to-check)

format属性告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规则对该函数的参数进行检查。“archetype”指定是哪种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。

具体使用格式如下:

__attribute__((format(printf,m,n)))

__attribute__((format(scanf,m,n)))

其中参数m与n的含义为:

m:第几个参数为格式化字符串(format string);

n:参数集合中的第一个,即参数“…”里的第一个参数在函数参数总数排在第几,注意,有时函数参数里还有“隐身”的呢,后面会提到;

在使用上,__attribute__((format(printf,m,n)))是常用的,而另一种却很少见到。下面举例说明,其中myprint为自己定义的一个带有可变参数的函数,其功能类似于printf:

//m=1;n=2

extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));

//m=2;n=3

extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));

需要特别注意的是,如果myprint是一个函数的成员函数,那么m和n的值可有点“悬乎”了,例如:

//m=3;n=4

extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));

其原因是,类成员函数的第一个参数实际上一个“隐身”的“this”指针。(有点C++基础的都知道点this指针,不知道你在这里还知道吗?)

__attribute__ noreturn

该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以避免出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式,如下所示:

extern void exit(int) __attribute__((noreturn));

extern void abort(void) __attribute__((noreturn));

__attribute__ const

该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外,其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。

为了说明问题,下面举个非常“糟糕”的例子,该例子将重复调用一个带有相同参数值的函数,具体如下:

extern int square(int n) __attribute__((const));

for (i = 0; i < 100; i++ )

{

total += square(5) + i;

}

通过添加__attribute__((const))声明,编译器只调用了函数一次,以后只是直接得到了相同的一个返回值。

同时使用多个属性

可以在同一个函数声明里使用多个__attribute__,并且实际应用中这种情况是十分常见的。使用方式上,你可以选择两个单独的__attribute__,或者把它们写在一起,可以参考下面的例子:

extern void die(const char *format, ...)

__attribute__((noreturn))

__attribute__((format(printf, 1, 2)));

section ("section-name")

属性 section 用于函数和变量,通常编译器将函数放在 .text 节,变量放在.data 或 .bss 节,使用 section 属性,可以让编译器将函数或变量放在指定的节中。

例如:

++++ include/linux/init.h

78: #define __init __attribute__ ((__section__ (".text.init")))

79: #define __exit __attribute__ ((unused, __section__(".text.exit")))

80: #define __initdata __attribute__ ((__section__ (".data.init")))

81: #define __exitdata __attribute__ ((unused, __section__ (".data.exit")))

82: #define __initsetup __attribute__ ((unused,__section__ (".setup.init")))

83: #define __init_call __attribute__ ((unused,__section__ (".initcall.init")))

84: #define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))

连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,

例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分内存。

* aligned (ALIGNMENT)

属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,

例如:

++++ include/asm-i386/processor.h

294: struct i387_fxsave_struct {

295: unsigned short cwd;

296: unsigned short swd;

297: unsigned short twd;

298: unsigned short fop;

299: long fip;

300: long fcs;

301: long foo;

......

308: } __attribute__ ((aligned (16)));

表示该结构类型的变量以 16 字节对齐。通常编译器会选择合适的对齐量,显示指

定对齐通常是由于体系限制、优化等原因。

* packed

属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用

于枚举、结构或联合类型时表示该类型使用最小的内存。例如:

++++ include/asm-i386/desc.h

51: struct Xgt_desc_struct {

52: unsigned short size;

53: unsigned long address __attribute__((packed));

54: };

域 address 将紧接着 size 分配。属性 packed 的用途大多是定义硬件相关的结

构,使元素之间没有因对齐而造成的空洞。

当前函数名

==========

GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码

中的名字,__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个

名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外

信息,Linux 内核只使用了 __FUNCTION__。

++++ fs/ext2/super.c

98: void ext2_update_dynamic_rev(struct super_block *sb)

99: {

100: struct ext2_super_block *es = EXT2_SB(sb)->s_es;

101:

102: if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)

103: return;

104:

105: ext2_warning(sb, __FUNCTION__,

106: "updating to rev %d because of new feature flag, "

107: "running e2fsck is recommended",

108: EXT2_DYNAMIC_REV);

这里 __FUNCTION__ 将被替换为字符串 "ext2_update_dynamic_rev"。虽然

__FUNCTION__ 看起来类似于标准 C 中的 __FILE__,但实际上 __FUNCTION__

是被编译器替换的,不象 __FILE__ 被预处理器替换。

内建函数

GNU C 提供了大量的内建函数,其中很多是标准 C 库函数的内建版本,例如

memcpy,它们与对应的 C 库函数功能相同,本文不讨论这类函数,其他内建函数

的名字通常以 __builtin 开始。

* __builtin_return_address (LEVEL)

内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址,参数

LEVEL 指定在栈上搜索框架的个数,0 表示当前函数的返回地址,1 表示当前函数

的调用者的返回地址,依此类推。例如:

++++ kernel/sched.c

437: printk(KERN_ERR "schedule_timeout: wrong timeout "

438: "value %lx from %p\n", timeout,

439: __builtin_return_address(0));

* __builtin_constant_p(EXP)

内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数

EXP 的值是常数,函数返回 1,否则返回 0。例如:

++++ include/asm-i386/bitops.h

249: #define test_bit(nr,addr) \

250: (__builtin_constant_p(nr) ? \

251: constant_test_bit((nr),(addr)) : \

252: variable_test_bit((nr),(addr)))

很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以

根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在

参数是常数时编译出最优化的代码。

* __builtin_expect(EXP, C)

内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表

达式 EXP 的值,C 的值必须是编译时常数。例如:

++++ include/linux/compiler.h

13: #define likely(x) __builtin_expect((x),1)

14: #define unlikely(x) __builtin_expect((x),0)

++++ kernel/sched.c

564: if (unlikely(in_interrupt())) {

565: printk("Scheduling in interrupt\n");

566: BUG();

567: }

这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排

语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中

断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证

经常执行的目标码更紧凑。

linux内核 extern,Linux内核之_attribute_相关推荐

  1. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  2. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  3. linux 应用层编程之内核链表list的使用

    linux内核提供了一个经典通用的双向循环链表list的实现,任何模块都可以借助该接口实现自己的内部循环链表.因为是通用的,可以直接移植到用户态中使用,下面介绍相关的接口与一个简单操作例子,包括链表的 ...

  4. linux 编译指cpu内核,linux内核编译与配置

    linux是如何组成的? 答:linux是由用户空间和内核空间组成的 为什么要划分用户空间和内核空间? 答:有关CPU体系结构,各处理器可以有多种模式,而LInux这样的划分是考虑到系统的 安全性,比 ...

  5. linux open dev/tty0 receive_buf,书写基于内核的linux键盘纪录器(p9-0e)(3)

    书写基于内核的linux键盘纪录器(p9-0e)(3) 2008-04-09 04:00:06来源:互联网 阅读 () 底层tty驱动调用receive_buf()这个函数用来发送硬件设备接收处理的字 ...

  6. Linux内核:VFIO 内核文档 (实例,API,bus驱动API)

    <ARM SMMU原理与IOMMU技术("VT-d" DMA.I/O虚拟化.内存虚拟化)> <提升KVM异构虚拟机启动效率:透传(pass-through).DM ...

  7. Linux用户态与内核态通信的几种方式(待完善)

    文章目录 1. 内核启动参数 2.模块参数与sysfs 3.sysctl 4.系统调用 5.netlink 6. procfs(/proc) 7.seq_file 8.debugfs 9.relayf ...

  8. Linux内核基础——Linux内核基础编程

    任务一.模块编程 子任务1.编写最简单的内核模块,并将其加载到内核中 hello.c #include<linux/module.h> #include<linux/kernel.h ...

  9. linux内核 thread,Linux内核线程kernel thread详解

    内核线程 为什么需要内核线程 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求). 内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的. 内核线程 ...

最新文章

  1. ubuntu设置securecrt串口权限
  2. (转)挺好的一篇介绍字节序的文章
  3. AJAX应用【股票案例、验证码校验】
  4. linux安装库文件下载,Linux下的Curses库的下载与安装
  5. npm install --save
  6. 带你理解Spring AOP
  7. python求出五位数的回文数_python之递归
  8. securecrt是什么工具_更效率、更优雅 | 阿里巴巴开发者工具不完全盘点
  9. 【Arcgis】绘制武汉市行政区划地图
  10. 按下()快捷键 可以迅速锁定计算机,电脑锁定的快捷键
  11. Visio对一个对象进行水平翻转
  12. 对接微信二维码支付(native方式)
  13. 大整数除一相对较小的数
  14. win10去除桌面快捷方式图标(亲测可用)
  15. 打2把王者荣耀的时间,学会JAVA自制验证码图片
  16. Excel表格导入导出功能实现
  17. linux kernel2.6中软中断运行线程ksoftirqd的创建
  18. Windows画图使用总结
  19. 用RJS写的检测用户名和email是否存在
  20. POJ-2528 Mayor's posters (离散化, 线段树)

热门文章

  1. 1000亿产值、2500万辆销售目标,范迪塞尔代言的雅迪将如何正确“出海”?
  2. android时间轴左右交叉布局,快速简单的定制一个时间轴布局(LinearLayout)
  3. html页面刷新回到顶部_HTMl页面中返回顶部的几种实现小结
  4. 如何保证战略落地_战略如何规划落地?值得借鉴
  5. 美国杜克大学计算机专业世界排名,美国杜克大学世界排名情况怎么样?
  6. 某计算机型号,某计算机的型号为486/33,其中33的含义是?
  7. 华为荣耀手机指令代码大全_双十二,华为/荣耀手机推荐选购指南,全系列横评推荐,那一款华为/荣耀手机最值得够买...
  8. 攻防世界-REverse-tt3441810(超简单秒懂)
  9. java反射克隆对象_Java反射 - 2(对象复制,父类域,内省)
  10. 学习Python一定要会的4个高阶函数