2016年(2)
分类: LINUX
2016-06-23 10:13:48
点击(此处)折叠或打开
以经典的hello_world模块为例,在模块实现文件中,我们编写了如下语句:
module_init(hello_init);
它会被编译器展开成如下:
在文件linux/init.h中定义了module_init.
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
那么在编译时刻,编译器会将其转化为如下语句
static initcall_t __initcall_hello_init6 __used __attribute__((__section__(".initcall6 .init"))) = hello_init;
实际上定义了一个类型为函数指针的变量__initcall_hello_init6,存放的是hello_init函数地址.
其中initcall_t是一个函数指针 typedef int (*initcall_t)(void); //linux/init.h中定义,
__used也是一个宏,在使用GCC3.4之前的编译器被展开成__attribute__((unused))来禁止编译器弹出有关函数没有被用到的的警告信息。在3.4之后被展开成__attribute__((used))功能一样
__attribute__((__section__(".initcall6 .init")))将该变量加载到段.initcall6.init上,在链接脚本头文件上可以找到如下语句:
#define INITCALLS \
*(.initcallearly.init)\
VMLINUX_SYMBOL(__early_initcall_end) = .;\
*(.initcall0.init)\
*(.initcall0s.init)\
*(.initcall1.init)\
*(.initcall1s.init)\
*(.initcall2.init)\
*(.initcall2s.init)\
*(.initcall3.init)\
*(.initcall3s.init)\
*(.initcall4.init)\
*(.initcall4s.init)\
*(.initcall5.init)\
*(.initcall5s.init)\
*(.initcallrootfs.init)\
*(.initcall6.init)\
*(.initcall6s.init)\
*(.initcall7.init)\
*(.initcall7s.init)
编译过内核后,生成的vmlinux.lds文件中有如下语句:
__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
当insmod的时候,内核从initcall6.init段中读取到该地址,然后跳转到该地址去执行,所以就打印出hello_init语句中实现的输出.
|
|