內核版本linux-2.6.22.6
在內核目錄include/linux/init.h中會解析,是否定義了MODULE宏來區分編譯進內核還是編譯成模塊。
編譯進內核的宏展開過程
module_init(x)->__initcall(x);->device_initcall(fn)->__define_initcall("6s",fn,6s)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn
宏展開後,驅動初始化函數就會帶有.initcall6.init屬性,那麼在其編譯時將會被安排到__initcall_start和__initcall_end範圍之間(定義在arch/cpu-type/kernel/vmlinux.lds.S中),之後由init/main.c的do_initcalls()函數進行統一的初始化。
static void __init do_initcalls(void)
{
initcall_t *call;
...
for (call = __initcall_start; call < __initcall_end; call++) {
result = (*call)();
}
...
}
編譯成模塊
module_init(x)
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
int init_module(void) __attribute__((alias(#initfn)));
用來告訴編譯器將init_module作爲#initfn的別名替代#initfn。
比如定義了一個led_init函數
static int __init led_init(void)
{
......
}
......
module_init(led_init);
......
經過module_init(led_init);之後led_init被別名init_module取代,所以可以理解爲:
int init_module( void) //static int __init led_init( void)被編譯器幹掉了,由int init_module( void)取代
{
......
}
......
這樣可以不用顯式的聲明init_module,而是使用更具有模塊化、人性化的名字led_init來讓人更容易理解的定義入口,之後insmod解析init_module入口執行之。