linux内核代码中,module_init(fn)在模块被链接到内核中的情况下,最终被宏替换如下:
#define module_init(fn) \
static initcall_t __initcall_##fn##6 __attribute_used__ \
__attribute__((__section__(".initcall6.init"))) = fn
我的理解是, 定义了一个指针指向fn, 这个指针变量链接时被保存在一个特定的位置.
'特定的位置'在vmlinux.lds中有定义
大致是这样:
__initcall_start = .;
.initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
*(.initcall0.init)
*(.initcall0s.init)
...
*(.initcall7.init)
*(.initcall7s.init)
}
__initcall_end = .;
这里可以说是把初始化用的函数划分为了14个级别, module_init的全部都在initcal6.init这一段.
然后 在init/main.c 中, 有do_initcalls这个函数
会从__initcall_start开始, 逐个根据函数指针调用每一个函数直到__initcall_end.
接下来是module_exit:
#define module_exit(x) __exitcall(x);
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __attribute_used__ \
__attribute__ ((__section__ (".exitcall.exit"))) = fn
跟module_init是一个意思, 只是'特殊位置'换成了.exitcall.exit.
如果是编译为模块, 则不一样了:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
利用gcc扩展定义了一个alias, 调用init_module就等同于调用initfn.
这样insmod无论是什么模块, 找init_module这个函数就可以了.