如有错误或遗漏,欢迎指正。
===========================
Linux 内核使用的 GNU C 扩展
===========================
GNC CC 是个功能很强大的跨平台 C 编译器,他对 C 语言提供了很多扩展,
这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。本文把
支持 GNU 扩展的 C 语言称为 GNU C。
Linux 内核代码使用了大量的 GNU C 扩展,以至于能够编译 Linux 内核的唯一编
译器是 GNU CC,以前甚至出现过编译 Linux 内核要使用特别的 GNU CC 版本的情
况。本文是对 Linux 内核使用的 GNU C 扩展的一个汇总,希望当您读内核源码遇
到不理解的语法和语义时,能从本文找到一个初步的解答,更周详的信息能够查看
gcc.info。文中的例子取自 Linux 2.4.18。
语句表达式
==========
GNU C 把包含在括号中的复合语句看做是个表达式,称为语句表达式,他能够出
现在任何允许表达式的地方,您能够在语句表达式中使用循环、局部变量等,原本
只能在复合语句中使用。例如:
include/linux/kernel.h
159: #define min_t(type,x,y) \
160: ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
net/ipv4/tcp_output.c
654: int full_space = min_t(int, tp->window_clamp, tcp_full_space(sk));
复合语句的最后一个语句应该是个表达式,他的值将成为这个语句表达式的值。
这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为:
#define min(x,y) ((x) < (y) ? (x) : (y))
这个定义计算 x 和 y 分别两次,当参数有副作用时,将产生不正确的结果,使用
语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。
Typeof
======
使用前一节定义的宏需要知道参数的类型,利用 typeof 能够定义更通用的宏,不
必事先知道参数的类型,例如:
include/linux/kernel.h
141: #define min(x,y) ({ \
142: const typeof(x) _x = (x); \
143: const typeof(y) _y = (y); \
144: (void) (&_x == &_y); \
145: _x < _y ? _x : _y; })
这里 typeof(x) 表示 x 的值类型,第 142 行定义了一个和 x 类型相同的局部变
量 _x 并初使化为 x,注意第 144 行的作用是检查参数 x 和 y 的类型是否相同。
typeof 能够用在任何类型能够使用的地方,通常用于宏定义。
零长度数组
==========
GNU C 允许使用零长度数组,在定义变长对象的头结构时,这个特性很有用。例
如:
include/linux/minix_fs.h
85: struct minix_dir_entry {
86: __u16 inode;
87: char name[0];
88: };
结构的最后一个元素定义为零长度数组,他不占结构的空间。在标准 C 中则需要
定义数组长度为 1,分配时计算对象大小比较复杂。
可变参数宏
==========
在 GNU C 中,宏能够接受可变数目的参数,就象函数相同,例如:
include/linux/kernel.h
110: #define pr_debug(fmt,arg...) \
111: printk(KERN_DEBUG fmt,##arg)
这里 arg 表示其余的参数,能够是零个或多个,这些参数连同参数之间的逗号构
成 arg 的值,在宏扩展时替换 arg,例如:
pr_debug("%s:%d",filename,line)
扩展为
printk("<7>" "%s:%d", filename, line)
使用 ## 的原因是处理 arg 不匹配任何参数的情况,这时 arg 的值为空,GNU
C 预处理器在这种特别情况下,丢弃 ## 之前的逗号,这样
pr_debug("success!\n")
扩展为
printk("<7>" "success!\n")
注意最后没有逗号。
标号元素
========
标准 C 需要数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通
过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在
初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,
例如:
arch/i386/kernel/irq.c
1079: static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
将数组的任何元素初使化为 ~0UL,这能够看做是一种简写形式。
要指定结构元素,在元素值前写 'FIELDNAME:',例如:
fs/ext2/file.c
41: struct file_operations ext2_file_operations = {
42: llseek: generic_file_llseek,
43: read: generic_file_read,
44: write: generic_file_write,
45: ioctl: ext2_ioctl,
46: mmap: generic_file_mmap,
47: open: generic_file_open,
48: release: ext2_release_file,
49: fsync: ext2_sync_file,
50 };
将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,
元素 read 初始化为 genenric_file_read,依次类推。我觉得这是 GNU C 扩展中
最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




