热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

当前位置 :| 主页>Linux教程>内核研究>

Linux 内核使用的 GNU C 扩展

来源: 作者: 时间:2007-12-27 Tag: 点击:
===========================
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")

注意最后没有逗号。


相关文章:
精通initramfs构建step by step
Linux利用kexec迅速切换内核
进程上下文VS中断上下文
内核通知链 学习笔记
linux spi子系统驱动分析
menuconfig 配置选项
《Linux操作系统内核实习》之练习一
udev详解
什么叫微内核,宏内核?
Linux 信号signal处理机制
开发简单的 Linux2.6 内核模块
删除内核的perl脚本
Linux2.6内核usb gadget驱动移植
GCC hacks in the Linux kernel
iomem
kernel学习的想法
让自己的驱动支持udev
linux内核编译步骤
内核的等待队列
Linux内核wait_queue深入分析
升级和删除内核
SD卡驱动分析2
Linux Kernel VDSO本地权限提升漏洞
内核中的TCP的追踪分析-15-TCP(IPV4)的客户端与
linux 2.6内核可加载模块的编译
内核模块HelloWorld
在环回接口上发送一个数据报
ARP初始化
1分钟编译FreeBSD内核
linux设备模型之uart驱动架构分析