一篇不错的关于GCC特性的文章
2017-11-15 09:34
549 查看
Linux 内核中的 GCC 特性
了解用于 C 语言的 GCC 扩展文章原来的地址:
https://www.ibm.com/developerworks/cn/linux/l-gcc-hacks/
GCC 和 Linux 是出色的组合。尽管它们是独立的软件,但是 Linux 完全依靠 GCC 在新的体系结构上运行。Linux 还利用 GCC 中的特性(称为扩展)实现更多功能和优化。本文讨论一些重要的扩展,讲解如何在 Linux 内核中使用它们。
GCC 当前的稳定版本(版本 4.3.2)支持 C 标准的三个版本:
International Organization for Standardization (ISO) 最初的 C 语言标准(ISO C89 或 C90)
带修正 1 的 ISO C90
当前的 ISO C99(这是 GCC 使用的默认标准,本文也假设采用这种标准)
注意:本文假设使用 ISO C99 标准。如果指定比 ISO C99 版本旧的标准,那么可能无法使用本文描述的一些扩展。可以在命令行上使用
-std选项指定 GCC 使用的实际标准。可以通过 GCC 手册查看哪个标准版本支持哪些扩展(见
参考资料 中的链接)。
可应用的版本
本文主要关注在 2.6.27.1 Linux 内核和 GCC 的 4.3.2 版本中使用 GCC 扩展。每个 C 扩展引用 Linux 内核源代码中的一个文件,可以在其中找到示例。
可以以几种方式对可用的 C 扩展进行分类。本文把它们分为两大类:
功能性 扩展提供新功能。
优化 扩展帮助生成更高效的代码。
功能性扩展
先讨论一些扩展标准 C 语言的 GCC 扩展。类型发现
GCC 允许通过变量的引用识别类型。这种操作支持泛型编程。在 C++、Ada 和 Java™ 语言等许多现代编程语言中都可以找到相似的功能。Linux 使用typeof构建
min和
max等依赖于类型的操作。清单 1 演示如何使用
typeof构建一个泛型宏(见 ./linux/include/linux/kernel.h)。
清单 1. 使用
typeof构建一个泛型宏
范围扩展
GCC 支持范围,在 C 语言的许多方面都可以使用范围。其中之一是switch/
case块中的
case语句。在复杂的条件结构中,通常依靠嵌套的
if语句实现与清单 2(见 ./linux/drivers/scsi/sd.c)相同的结果,但是清单 2 更简洁。使用
switch/
case也可以通过使用跳转表实现进行编译器优化。
清单 2. 在
case语句中使用范围
spinlock_t创建一个大小为
LOCK_COUNT的数组。数组的每个元素初始化为
SPIN_LOCK_UNLOCKED值。
零长度的数组
在 C 标准中,必须定义至少一个数组元素。这个需求往往会使代码设计复杂化。但是,GCC 支持零长度数组的概念,这对于结构定义尤其有用。这个概念与 ISO C99 中灵活的数组成员相似,但是使用不同的语法。下面的示例在结构的末尾声明一个没有成员的数组(见 ./linux/drivers/ieee1394/raw1394-private.h)。这允许结构中的元素引用结构实例后面紧接着的内存。在需要数量可变的数组成员时,这个特性很有用。
判断调用地址
在许多情况下,需要判断给定函数的调用者。GCC 提供用于此用途的内置函数__builtin_return_address。这个函数通常用于调试,但是它在内核中还有许多其他用途。
如下面的代码所示,
__builtin_return_address接收一个称为
level的参数。这个参数定义希望获取返回地址的调用堆栈级别。例如,如果指定
level为
0,那么就是请求当前函数的返回地址。如果指定
level为
1,那么就是请求进行调用的函数的返回地址,依此类推。
local_bh_disable函数在本地处理器上禁用软中断,从而禁止在当前处理器上运行 softirqs、tasklets 和 bottom halves。使用
__builtin_return_address捕捉返回地址,以便在以后进行跟踪时使用这个地址。
常量检测
在编译时,可以使用 GCC 提供的一个内置函数判断一个值是否是常量。这种信息非常有价值,因为可以构造出能够通过常量叠算(constant folding)优化的表达式。__builtin_constant_p函数用来检测常量。
__builtin_constant_p的原型如下所示。注意,
__builtin_constant_p并不能检测出所有常量,因为 GCC 不容易证明某些值是否是常量。
roundup_pow_of_two宏。如果发现表达式是常量,那么就使用可以优化的常量表达式。如果表达式不是常量,就调用另一个宏函数把值向上取整到 2 的幂。
清单 3. 使用常量检测优化宏函数
函数属性
GCC 提供许多函数级属性,可以通过它们向编译器提供更多数据,帮助编译器执行优化。本节描述与功能相关联的一些属性。下一节描述影响优化的属性。
如清单 4 所示,属性通过其他符号定义指定了别名。可以以此帮助阅读源代码参考,了解属性的使用方法(见 ./linux/include/linux/compiler-gcc3.h)。
清单 4. 函数属性定义
always_inline让 GCC 以内联方式处理指定的函数,无论是否启用了优化。
deprecated指出函数已经被废弃,不应该再使用。如果试图使用已经废弃的函数,就会收到警告。还可以对类型和变量应用这个属性,促使开发人员尽可能少使用它们。
__used__告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。这对于从汇编代码中调用 C 函数有帮助。
__const__告诉编译器某个函数是无状态的(也就是说,它使用传递给它的参数生成要返回的结果)。
warn_unused_result让编译器检查所有调用者是否都检查函数的结果。这确保调用者适当地检验函数结果,从而能够适当地处理错误。
下面是在 Linux 内核中使用这些属性的示例。
deprecated示例来自与体系结构无关的内核(./linux/kernel/resource.c),
const示例来自 IA64 内核源代码(./linux/arch/ia64/kernel/unwind.c)。
优化扩展
现在,讨论有助于生成更好的机器码的一些 GCC 特性。分支预测提示
在 Linux 内核中最常用的优化技术之一是__builtin_expect。在开发人员使用有条件代码时,常常知道最可能执行哪个分支,而哪个分支很少执行。如果编译器知道这种预测信息,就可以围绕最可能执行的分支生成最优的代码。
如下所示,
__builtin_expect的使用方法基于两个宏
likely和
unlikely(见 ./linux/include/linux/compiler.h)。
__builtin_expect,编译器可以做出符合提供的预测信息的指令选择决策。这使执行的代码尽可能接近实际情况。它还可以改进缓存和指令流水线。
例如,如果一个条件标上了 “likely”,那么编译器可以把代码的 True 部分直接放在分支指令后面(这样就不需要执行分支指令)。通过分支指令访问条件结构的 False 部分,这不是最优的方式,但是访问它的可能性不大。按照这种方式,代码对于最可能出现的情况是最优的。
清单 5 给出一个使用
likely和
unlikely宏的函数(见 ./linux/net/core/datagram.c)。这个函数预测
sum变量将是零(数据包的
checksum是有效的),而且
ip_summed变量不等于
CHECKSUM_HW。
清单 5. likely 和 unlikely 宏的使用示例
预抓取
另一种重要的性能改进方法是把必需的数据缓存在接近处理器的地方。缓存可以显著减少访问数据花费的时间。大多数现代处理器都有三类内存:一级缓存通常支持单周期访问
二级缓存支持两周期访问
系统内存支持更长的访问时间
为了尽可能减少访问延时并由此提高性能,最好把数据放在最近的内存中。手工执行这个任务称为预抓取。GCC 通过内置函数
__builtin_prefetch支持数据的手工预抓取。在需要数据之前,使用这个函数把数据放到缓存中。如下所示,
__builtin_prefetch函数接收三个参数:
数据的地址
rw参数,使用它指明预抓取数据是为了执行读操作,还是执行写操作
locality参数,使用它指定在使用数据之后数据应该留在缓存中,还是应该清除
清单 6. 范围预抓取的包装器函数
变量属性
除了本文前面讨论的函数属性之外,GCC 还为变量和类型定义提供了属性。最重要的属性之一是aligned属性,它用于在内存中实现对象对齐。除了对于性能很重要之外,某些设备或硬件配置也需要对象对齐。
aligned属性有一个参数,它指定所需的对齐类型。
下面的示例用于软件暂停(见 ./linux/arch/i386/mm/init.c)。在需要页面对齐时,定义
PAGE_SIZE对象。
packed属性打包一个结构的元素,从而尽可能减少它们占用的空间。这意味着,如果定义一个
char变量,它占用的空间不会超过一字节(8 位)。位字段压缩为一位,而不会占用更多存储空间。
这段源代码使用一个
__attribute__声明进行优化,它用逗号分隔的列表定义多个属性。
清单 7. 结构打包和设置多个属性
结束语
本文只讨论了在 Linux 内核中可以使用的几个 GCC 特性。可以通过 GNU GCC 手册进一步了解针对 C 和 C++ 语言的所有扩展(见参考资料 中的链接)。另外,尽管 Linux 内核经常使用这些扩展,但是也可以在用户自己的应用程序中使用它们。随着 GCC 的发展,肯定会出现新的扩展,它们会进一步改进性能和增加 Linux 内核的功能。
相关主题
GNU Compiler Collection 提供关于 GCC 的所有信息。在这里,可以找到新闻和最新的 GCC 源代码(包括以前的和最新的发行版)。还可以找到每个版本的详细历史和在线文档,这可以提供 GCC 和所有扩展的详细信息。
Experimental GCCextensions 是 GNU 维护的扩展列表,标准的发行版还不包含这些扩展。可以通过它了解正在开发的 GCC 扩展。
阅读文章 “认识 GCC 4”(developerWorks,2008 年 10 月),了解关于 GCC 的第四个主要版本(GCC4)的更多信息。这篇文章介绍 GCC4 以及它相对于前四个次要版本的改进。
在 developerWorks Linux 专区 寻找为 Linux 开发人员(包括
Linux 新手入门)准备的更多参考资料,查阅我们
最受欢迎的文章和教程。
在 developerWorks 上查阅所有
Linux 技巧 和
Linux 教程。
相关文章推荐
- [转] Fast Fourier transform — FFT (一篇不错的关于一维FFT原理及CPU实现的文章)
- 关于JSONP和JSON(一篇写的不错的文章)
- 一篇关于ReadWriteLock不错的文章
- 关于委托的一篇不错的文章(C# 中的委托和事件)
- 整理一篇不错的关于软件加密的文章
- 关于linux中文乱码一篇不错滴文章(不过稍微有点啰嗦,有机会整理下)
- 关于Servlet的多线程,看到一篇不错的文章,关注一下
- 关于有限自动机的一篇不错的文章
- 一篇不错的关于js"闭包"的文章
- 关于Server Application Unavailable错误的一篇不错的文章
- 非常不错的一篇关于java多线程编程总结的文章
- 转载一篇个人觉得不错的关于对象引用的文章
- 一篇不错的关于freebsd双线路负载均衡的文章
- 不错的一篇关于强迫症的文章
- 关于java多线程的一篇不错的入门级文章
- 感觉一篇关于R树不错的文章,值得推荐
- 一篇关于Android Root不错的文章
- 一篇关于GCC的转载的文章 很好。。。。
- iteye一篇关于同步锁的一个比较不错的文章,入门用
- 关于有限自动机的一篇不错的文章[转载]