module_init的加载过程

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个级别中。排行倒数第三

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值