几乎所有的驱动都要用到module_init,为什么都要用到module_init呢?module_init做
了哪些事情呢?我们来分下module_init的代码。
module_init函数原型在当中,可以找到如下代码:
注意:module_exit在编译进内核的时侯投有意因为静态编译的驱动无法卸载!所所以只分析module_init。
include/linux/init.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 */
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __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 __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
#define __setup_param(str, unique_id, fn) /* nothing */
#define __setup(str, func) /* nothing */
#endif
对于module_init的定义是一个条件编译,如果没有定义MODULE,则module_init为
initcall(x);如果定义了MODULE
,module_init为int init_module(void) attribute((alias(#initfn)));
MODULE是由顶层Makefile定义的,打开内核源码下的顶层Makefile,
驱动编译进内核KBUILD_AFLAGS_KERNEL决定
驱动编译成模块KBUILD_AFLAGS_MODULE决定
所以这里时驱动编译成模块
即
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(__initcall_##fn##id)
注意:initcall_t是函数指针,原型如下
typedef int (*initcall_t)(void);
所以,当时使用module_init宏定义接囗以后,会声明了一个initcall_helloworld6函数指针变量。將这个函数指针初始化为hello_world,编译的时候将这个函数指针放在.initcall6.init段中。
内核中很多驱动都用了module_init,这些函数指针会按照编译先后顺序放在.initcall6.init段中。
除了module_init,还有其他的宏定义接囗,他们的原型都是defin_initcall,区别就是优先级不一样,也就是系统启动的时侯的启动顺序不一样,module_init的优先级是6。
include/linux/init.h
#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)
注:这里的##level##代表就是组1,2,3,…
打开include/asm-generic/vmlinux.lds.h文件。找到以下代码
#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
*(.initcall##level##.init) \
*(.initcall##level##s.init) \
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
*(.initcallearly.init) \
INIT_CALLS_LEVEL(0) \
INIT_CALLS_LEVEL(1) \
INIT_CALLS_LEVEL(2) \
INIT_CALLS_LEVEL(3) \
INIT_CALLS_LEVEL(4) \
INIT_CALLS_LEVEL(5) \
INIT_CALLS_LEVEL(rootfs) \
INIT_CALLS_LEVEL(6) \
INIT_CALLS_LEVEL(7) \
VMLINUX_SYMBOL(__initcall_end) = .;
将INIT-CALLS宏展开以后:
#define INIT_CALLS \
__initcall_start = .; \
*(.initcallearly.init) \
__initcall0_start = .; \
*(.initcall0.init) \
*(.initcall0s.init) \
INIT_CALLS_LEVEL(1) \
INIT_CALLS_LEVEL(2) \
INIT_CALLS_LEVEL(3) \
INIT_CALLS_LEVEL(4) \
INIT_CALLS_LEVEL(5) \
__initrootfs_start = .; \
*(.initrootfs.init) \
*(.initrootfss.init) \
__initcall6_start = .; \
*(.initcall6.init) \
*(.initcall6s.init) \
INIT_CALLS_LEVEL(7) \
VMLINUX_SYMBOL(__initcall_end) = .;
所以宏INIT_CALLS的作用就是相同等级的段会放在同一块内存区域,不同等级的块的内存区域会按照等级的大小依次链接在一起。
这里的__initcall0_startt是一个变量。__initcall0_start中记录着__initcall0_start段的首地址。这个__initcall0_start变量在哪用的呢?在init/main.c中,通过extern的方法使用了
__initcall0_start等变量。然后将这些首地址放在了数组initcall_levels中。
init/main.c
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
.initcall##level##.init段中的函数最终会在do_one_initcall函数中执行。调用关系如下所示
start_kernel定义在init/main.c当中,大家可以自己上按照上图中的调用关系追踪。
do_initcalls定义在init/main.c文件当中。我们找到以下代码,以下代码为核心代码:
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
这个for循环一共会循环执行7次do_initcall_level从for循环中我们看出来0的优先级要大于1,数字越小优先级越高。相同等级带s的优先级要小于不带s的优先级。
在do_initcall_level里面执行每一个段中的保存的函数指针
static void __init do_initcall_level(int level)
{
initcall_t *fn;
strcpy(initcall_command_line, saved_command_line);
parse_args(initcall_level_names[level],
initcall_command_line, __start___param,
__stop___param - __start___param,
level, level,
&repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
}
使用比modue_init优先级更高的函数:
#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)