在 linux 内核源代码中,与段相关的重要宏定义有:
__init , __initdata, __exit, __exitdata 及类似的宏。
在 include/init.h 中可以看到:
__init 宏常用的地方是驱动模块初始化函数的定义处;
__initdata 常用于数据定义,目的是将数据放入名叫 .init.data 的输入段。
需要注意的是,上面的定义中,用 __section__ 代替了 section 。还有其他一些类似定义的宏,作用也类似。
........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ........... ...........
关于 initcall 的宏定义
这条宏定义更为重要,它是一条可扩展的宏:
上面 initcall_t 的定义为:
__used 的定义在 include/linux/compiler-gcc4.h 中找到(根据编译器的不同,gcc4 中的 4 可能为 3)为:
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_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=后面的内容传给该处理函数。
__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=后面的内容传给该处理函数。