linux中,模块的初始化都利用module_init()函数来完成,下面主要对以build in 方式编译的模块的初始化的实现进行简介。
例如:
module_init(init_ext2_fs)
static int __init init_ext2_fs(void)
{
int err = init_ext2_xattr();
if (err)
return err;
err = init_inodecache();
if (err)
goto out1;
err = register_filesystem(&ext2_fs_type);
if (err)
goto out;
return 0;
out:
destroy_inodecache();
out1:
exit_ext2_xattr();
return err;
}
开始一直对初始化函数前面的 __init不理解,gcc又一扩展?直到看了内核中init.h和内核的ld script后才略明白,其实现在对内核的编译流程以及内核镜像的制作依然不太懂,慢慢来,每天进步一点点^_^
在include/linux目录下有一个init.h,这个头文件中的很多内容说明了模块的初始化函数如何被编译,即指明 初始化函数和section的对应关系,如果对elf文件、编译、链接知识不太懂的同学可以先查看相关资料。
/* These macros are used to mark some functions or
* initialized data (doesn't apply to uninitialized data)
* as `initialization' functions. The kernel can take this
* as hint that the function is used only during the initialization
* phase and free up used memory resources after
*这些所谓的初始化函数会在系统初始化阶段使用,使用后即从内存中free up。如何和为什么free up,还不太了解^_^
* Usage:
* For functions:
*
* You should add __init immediately before the function name, like:
*你必须在函数名前加上 __init,表面这个函数是一个初始化函数
* static void __init initme(int x, int y)
* {
* extern int z; z = x * y;
* }
*
那么到底module_init是什么呢
/**
* 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_init宏定义,是内核模块按照build in方式编译,而不是module方式,module方式在这个头文件中也有说明,不介绍了。
#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
typedef int (*initcall_t)(void); //initcall_t是一个函数指针int (*)(void)
这里碰到一个 __attribute__,它和__section__一起告诉编译器,凡是被他们修饰的函数或者变量应该放在特殊的section里面。
上面这个宏就定义了一个“变量”,不过这个变量是一个函数指针,并且这个函数会被编译器放在.initcall" level ".init section里面。继续回到module_init(),将所有宏展开后,
module_init(fn) -----> __define_initcall("6",fn,6) ------> __initcall_##fn##6 = fn 将函数fn 放在.initcal6.init section里面,这个section会在后续ld scrips中用到。
对于ext2文件系统的初始化函数
module_init(init_ext2_fs),即定义了一个 __initcall_init_ext2_fs_6的函数,这个函数其实和init_ext2_fs是一个函数
在arch/x86/kernel中的vmlinux_32.lds.S即为x86 32架构下的内核ld scrips,其中
/*
add by lyf
#define INITCALLS \
*(.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.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
__initcall_start = .;
INITCALLS
__initcall_end = .;
}
上述命令即说明了将这些初始化函数装载位置,在__initcall_start和 __initcall_end之间,而上面提到的.initcal6.init当然也在其中。