您的位置:首页 > 运维架构 > Linux

GNU C语言的 扩展(八) linux 内核 段相关宏

2014-10-19 11:50 169 查看
在 linux 内核源代码中,与段相关的重要宏定义有:

__init , __initdata, __exit, __exitdata 及类似的宏。

在 include/init.h 中可以看到:

引用
#define __init __attribute__ ((__section__ (".init.text"))) __cold

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

#define __exitdata __attribute__ (( __section__ (".exit.data")))

#define __exit_call __attribute_used__ __attribute__ (( __section__ (".exitcall.exit")))

#define __init_refok oninline __attribute__ ((__section__ (".text.init.refok")))

#define __initdata_refok __attribute__ ((__section__ (".data.init.refok")))

#define __exit_refok noinline __attribute__ ((__section__ (".exit.text.refok")))

.........

#ifdef MODULE

#define __exit __attribute__ (( __section__ (".exit.text"))) __cold

#else

#define __exit __attribute_used__ __attribute__ ((__section__ (".exit.text"))) __cold

#endif

__init 宏常用的地方是驱动模块初始化函数的定义处;

__initdata 常用于数据定义,目的是将数据放入名叫 .init.data 的输入段。

需要注意的是,上面的定义中,用 __section__ 代替了 section 。还有其他一些类似定义的宏,作用也类似。

........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ...........

关于 initcall 的宏定义

这条宏定义更为重要,它是一条可扩展的宏:

引用
#define __define_initcall(level,fn,id) static initcall_t __initcall_##fn##id __used __attribute__((__section__(".initcall" level ".init"))) = fn

上面 initcall_t 的定义为:

引用
typedef
int
(*initcall_t)(void);

__used 的定义在 include/linux/compiler-gcc4.h 中找到(根据编译器的不同,gcc4 中的 4 可能为 3)为:

引用
#define __used __attribute__((__used__)

initcall 宏定义带有 3 个参数:

level, fn, id

分析一下这个宏:

由上面知道,initcall_t 是个用来函数指针定义类型,所以 __initcall_##fn##id 就是一个函数指针,fn 则是一个已经定义好了的函数。这里 ## 符号表示一个连接符的作用,它实际上负责一个新的函数名的定义。先不考虑 __used , __attribute__ 这些声明,假设fn 是一个定义好的函数 func() 的函数名 func,id 值为 9,level 值为 7,那么经过宏定义并展开后变成:

static initcall_t __initcall_func9

这时,再考虑 __used , __attribute__ 这些声明的意义:

__attribute__((__section__(".initcall" level ".init"))) 表示,函数(以上面的 __initcall_func9 为例)被放在 .initcall7.init 这个 section 中;__used 表示使用 .initcall7.init 这个 section 中的空间。

上面宏定义并不直接使用,同样在 init.h 文件中找到如下的宏定义:

引用
#define core_initcall(fn) __define_initcall("1",fn,1)

#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)

#define postcore_initcall(fn) __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)

#define arch_initcall(fn) __define_initcall("3",fn,3)

#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)

#define subsys_initcall(fn) __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)

#define fs_initcall(fn) __define_initcall("5",fn,5)

#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn) __define_initcall("6",fn,6)

#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)

#define late_initcall(fn) __define_initcall("7",fn,7)

#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)

这些宏定义是为了方便使用 __define_initcall 宏的,上面每条宏第一次使用时都会产生一个新的输入段。

... ... ... ... ...... .... ... ... ... ... .... ... ...

(转)

__setup宏的来源及使用__setup这条宏在LinuxKernel中使用最多的地方就是定义处理Kernel启动参数的函数及数据结构,请看下面的宏定义:

#define __setup_param(str, unique_id, fn,early) \

static char __setup_str_##unique_id[] __initdata__aligned(1) = str; \

static struct obs_kernel_param__setup_##unique_id \

__used__section(.init.setup) \

__attribute__((aligned((sizeof(long))))) \

= { __setup_str_##unique_id, fn, early }

#define __setup(str,fn) \

__setup_param(str, fn, fn, 0)

使用Kernel中的例子分析一下这两条定义:

__setup("root=",root_dev_setup);

这条语句出现在init/do_mounts.c中,其作用是处理Kernel启动时的像root=/dev/mtdblock3之类的参数的。

分解一下这条语句,首先变为:

__setup_param("root=",root_dev_setup,root_dev_setup,0);

继续分解,将得到下面这段代吗:

static char __setup_str_root_dev_setup_id[] __initdata__aligned(1) = "root=";

static struct obs_kernel_param __setup_root_dev_setup_id

__used __section(.init.setup)

__attribute__((aligned((sizeof(long)))))

= { __setup_str_root_dev_setup_id,root_dev_setup, 0 };

这段代码定义了两个变量:字符数组变量__setup_str_root_dev_setup_id,其初始化内容为"root=",由于该变量用__initdata修饰,它将被放入.init.data输入段;另一变量是结构变量__setup_root_dev_setup_id,其类型为structobs_kernel_param, 该变理被放入输入段.init.setup中。结构struct structobs_kernel_param也在该文件中定义如下:

struct obs_kernel_param {

const char *str;

int (*setup_func)(char *);

int early;

};

变量__setup_root_dev_setup_id的三个成员分别被初始化为:

__setup_str_root_dev_setup_id -->前面定义的字符数组变量,初始内容为"root="。

root_dev_setup --> 通过宏传过来的处理函数。

0 -->常量0,该成员的作用以后分析。

现在不难想像内核启动时怎么处理启动参数的了:通过__setup宏定义obs_kernel_param结构变量都被放入.init.setup段中,这样一来实际是使.init.setup段变成一张表,Kernel在处理每一个启动参数时,都会来查找这张表,与每一个数据项中的成员str进行比较,如果完全相同,就会调用该数据项的函数指针成员setup_func所指向的函数(该函数是在使用__setup宏定义该变量时传入的函数参数),并将启动参数如root=后面的内容传给该处理函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: