module_init 是 Linux 内核模块机制中的一个宏,用于指定模块的初始化函数。当内核加载该模块时,这个初始化函数将被调用。理解 module_init 的原理有助于深入理解 Linux 内核模块的加载和初始化机制。
module_init 的定义
在 Linux 内核中,module_init 宏的定义如下:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
__initcall(__inittest)
其中,initfn
是模块的初始化函数,通常由开发者定义。这个宏主要做了两件事:
定义一个静态内联函数 __inittest
:这个函数返回 initfn
,即开发者定义的初始化函数。
调用 __initcall
:将 __inittest
注册为一个初始化调用。
__initcall
宏
__initcall
宏是用于将初始化函数注册到一个特殊的内核段(section),这个段中的函数将在内核或模块初始化时被调用。它的定义如下:
#define __initcall(fn) __define_initcall(fn, 6)
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
__initcall
宏通过 __define_initcall
将函数指针 fn
放入一个特定的 .initcall
段中。fn
在这里就是 __inittest
函数。.initcall
段的命名是由 id
决定的,不同的初始化优先级会对应不同的 id
。
初始化函数的调用顺序
Linux 内核中有不同的初始化阶段,每个阶段有不同的优先级,这些优先级对应不同的段(如 .initcall1.init
、.initcall2.init
等)。module_init
使用的优先级为 6,因此初始化函数会被放置在 .initcall6.init
段。
当内核或模块加载时,内核会遍历这些 .initcall
段,并调用其中注册的所有函数,从而执行模块的初始化逻辑。
工作原理总结
定义初始化函数:开发者定义一个模块初始化函数 initfn
。
使用 module_init
注册初始化函数:module_init
宏将 initfn
注册到 .initcall6.init
段。
内核加载模块:当内核加载该模块时,内核会遍历 .initcall
段,并调用所有注册的初始化函数,包括模块的 initfn
。
执行初始化逻辑:initfn
执行模块的初始化逻辑,如资源申请、数据结构初始化等。
例子
#include <linux/module.h>
#include <linux/init.h>
static int __init my_driver_init(void)
{
printk(KERN_INFO "My driver is initialized\n");
return 0;
}
module_init(my_driver_init);
在这个例子中:
my_driver_init
是模块的初始化函数。
module_init(my_driver_init)
将 my_driver_init
注册为模块加载时的初始化函数。
当模块加载时,内核会调用 my_driver_init
,执行其中的初始化代码。
总结
module_init
宏通过将模块的初始化函数注册到内核的 .initcall
段,使得在模块加载时,内核能够自动调用这些初始化函数。这个机制使得模块的初始化过程得以统一管理,并确保模块加载时必要的初始化操作能够正确执行。