标号元素
========
标准 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 扩展中
最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然
保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。
Case 范围
=========
GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:
++++ arch/i386/kernel/irq.c
1062: case ''''0'''' ... ''''9'''': c -= ''''0''''; break;
1063: case ''''a'''' ... ''''f'''': c -= ''''a''''-10; break;
1064: case ''''A'''' ... ''''F'''': c -= ''''A''''-10; break;
case ''''0'''' ... ''''9'''':
相当于
case ''''0'''': case ''''1'''': case ''''2'''': case ''''3'''': case ''''4'''':
case ''''5'''': case ''''6'''': case ''''7'''': case ''''8'''': case ''''9'''':
声明的特殊属性
==============
GNU C 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代
码检查。要指定一个声明的属性,在声明后写
__attribute__ (( ATTRIBUTE ))
其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。GNU C 支持十几个属性,这
里介绍最常用的:
* noreturn
属性 noreturn 用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的
代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:
++++ include/linux/kernel.h
47: # define ATTRIB_NORET __attribute__((noreturn)) ....
61: asmlinkage NORET_TYPE void do_exit(long error_code)
ATTRIB_NORET;
* format (ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)
属性 format 用于函数,表示该函数使用 printf, scanf 或 strftime 风格的参
数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定 format 属性可以
让编译器根据格式串检查参数类型。例如:
++++ include/linux/kernel.h?
89: asmlinkage int printk(const char * fmt, ...)
90: __attribute__ ((format (printf, 1, 2)));
表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
* unused
属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免
编译器产生警告信息。
* 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 行的目标码可能会放在较远的位置,以保证
经常执行的目标码更紧凑。
本文来自: () 详细出处参考:http:///html/article/kernel/20071227/9418_3.html