module_init这个网上讲解的一大堆,这里作个学习记录。
initcall加载是分两步的
第一步:加载early_initcall
start_kernel->
rest_init->
kernel_init->
kernel_init_freeable->
do_pre_smp_initcalls
第二步:加载从__initcall0_start到__initcall7_start
start_kernel->
rest_init->
kernel_init->
kernel_init_freeable->
do_basic_setup->
do_initcalls
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
#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) = .;
这里要注意的,这个数组里面并没有
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
__initcallrootfs_start的引用,所以按道理rootfs_initcall的定义不会加载,但是我这个代码是从官网上下载的原始代码,可能不涉及文件系统加载吧。搜索代码也没看到其它哪里单独加载了__define_initcall(fn, rootfs)这个。所以我认为实际中,需要把__initcallrootfs_start添加到__initcall5_start这个的后面,不知道理解是否正确
首先module_init的定义是在文件:
include/linux/module.h
下面来看看具体定义
#ifndef MODULE
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
#else /* MODULE */
#define early_initcall(fn) module_init(fn)
#define core_initcall(fn) module_init(fn)
#define core_initcall_sync(fn) module_init(fn)
#define postcore_initcall(fn) module_init(fn)
#define postcore_initcall_sync(fn) module_init(fn)
#define arch_initcall(fn) module_init(fn)
#define subsys_initcall(fn) module_init(fn)
#define subsys_initcall_sync(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define fs_initcall_sync(fn) module_init(fn)
#define rootfs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn)
#define device_initcall_sync(fn) module_init(fn)
#define late_initcall(fn) module_init(fn)
#define late_initcall_sync(fn) module_init(fn)
#define console_initcall(fn) module_init(fn)
#define security_initcall(fn) module_init(fn)
/* 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)));
#endif
上面宏定义:
1.如果没有定义了MODULE,那么就用 include/linux/init.h文件里面__initcall宏定义,等会具体看看这个宏
2.如果有定义MODULE,那么各种initcall全部被定义成module_init。不会存在我们印像中的加载顺序了。因为MODULE一般是针对单独编译的ko文件,就无所谓加载顺序,module_init宏定义,看起来了就是为回调函数起了个别名 init_module,在insmod时候统一调用init_module
3.当然现在一般都不会使用MODULE,很早之前做驱动时经常会说内核裁剪。裁剪的原因大体上就是节省空间而ko文件也在那时候很流行。现在硬件空间已经不是问题了,基本上没听说过内核裁剪的话题了。由于DTS流行,基本上也不需要内核裁剪,ko文件好像也已经绝迹了。大概是因为ko文件不太安全,污染内核
下面来看看include/linux/init.h里面定义的各种initcall
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(__initcall_##fn##id)
#define early_initcall(fn) __define_initcall(fn, early)
#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)
#define __initcall(fn) device_initcall(fn)
上面定义了17种加载级别,宏定义__define_initcall。我们拿级别为1的来展开看看
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
core_initcall(xxx)
static initcall_t __initcall_xxx1 __used
attribute((section(".initcall" 1 “.init”))) = xxx;
LTO_REFERENCE_INITCALL(__initcall_xxx1)
core_initcall_sync(xxx)
static initcall_t __initcall_xxx1s __used
attribute((section(".initcall" 1s “.init”))) = xxx;
LTO_REFERENCE_INITCALL(__initcall_xxx1s)
要注意级1和级1S是两个级别,1先调,1S后调。下面来看看
文件:include/ams-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_LEVEL这个宏定义,拿INIT_CALLS_LEVEL(0) 展开
VMLINUX_SYMBOL(__initcal1_start) = .; \
*(.initcall1.init) \
*(.initcal1s.init) \
可以看出每次INIT_CALLS_LEVEL是涉及到两个级别的调用。一个n和一个ns,initcallearly.init最先调用对应的宏定义为:
#define early_initcall(fn) __define_initcall(fn, early) //所有initcall中级别最高
下面回过头来看看我们经常用的module_init
#define module_init(x) __initcall(x); //不使用MODULE定义
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
可以看到,module_init的级别是比较低的,在17个级别中。排行倒数第三
本文详细解析了Linux内核中的模块初始化过程,包括module_init宏的定义与作用,以及不同初始化级别的实现机制。通过分析源代码,揭示了从early_initcall到late_initcall的加载顺序,解释了MODULE定义下initcall的统一处理方式。
9185

被折叠的 条评论
为什么被折叠?



