module_init与module_exit宏定义在include/linux/module.h文件中,部分代码如下:
#ifndef MODULE
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module. If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
#else /* MODULE */
...
...
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __maybe_unused __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
#endif
宏定义MODULE在静态编译链接时不定义,在动态编译链接时定义。通过Makefile中obj+y(静态编译)或obj+m(动态编译)来区分。
当采用静态编译时,module_init定义如下:
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
__initcall和__exitcall宏定义在include/linux/init.h中,如下:
#define __initcall(fn) device_initcall(fn)
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
module_exit宏到此展开完成,当我们调用module_exit(my_exit)时,结果如下:
static exitcall_t __exitcall_my_exitl = my_exit
接下来继续对device_initcall展开,device_initcall的宏定义如下:
#define device_initcall(fn) __define_initcall(fn, 6)
继续对__define_initcall展开,如下:
#define __define_initcall(fn, id) \
static initcall_t __initcall_name(fn, id) __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
__initcall_name展开如下:
#define __initcall_name(fn, id) __initcall_##fn##id
至此,module_init完全展开。
在静态编译过程中,如果使用module_init(my_init),结果如下:
static initcall_t __initcall_my_init_6 __used __attribute__((__section__(".initcall6.init"))) = my_init
该语句的作用:定义了一个initcall_t函数指针变量,指针指向自己定义的初始化函数。该指针变量在链接时会被放到section(.initcall6.init)。
而初始化函数被调用的过程可以参照以下链接:https://blog.csdn.net/jzjhome/article/details/72764931
当采用动态编译时,module_init和module_exit定义如下:
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
#define module_exit(exitfn) \
static inline exitcall_t __maybe_unused __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
这两个宏的主要作用就是给输入的函数取别名:将init_module定义为initf函数n的别名,即定义一个init_module变量,指向initfn;将cleanup_module定义为exitfn函数的别名,即定义一个cleanup_module变量,指向exitfn。
具体的编译和加载过程可以参照以下链接:https://blog.csdn.net/yueqian_scut/article/details/46694229
除了module_init外,还有一些与init相关的宏定义:
/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
* Keep main.c:initcall_level_names[] in sync.
*/
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
其过程基本类似,只是在链接的过程中会被放到不同的段中,从而导致调用的顺序不一样。调用顺序是从.initcall1.init段到.initcall7.init依次进行的,但在各个子段内,函数的调用顺序与链接顺序相关,所以是不确定的。