编译进内核的驱动系统是如何运行的?
module_init
几乎所有的驱动都要用到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)
以hello_world为例
注意: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)
__initcall##level##_start关联到".initcall##level##.init"段和".initcall##level##s.init"段
注:这里的##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段中的函数
.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)