module_init宏分析


一直在使用module_init和module_exit宏,但是对于这两个宏依然比较陌生,不知道它到底为程序员做了什么东西,今天闲来无事,就打开source insight分析了下它的具体实现,瞬间明白了,现在将笔记整理下。以经典的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语句中实现的输出.

相关内容

    暂无相关文章