【Android底层学习总结】4. 内核中do_initcalls函数源码解析

1 前言

本节内容主要对do_initcalls函数进行总结,老实说这个函数有点复杂,需要了解汇编知识以及C语言里面对宏、函数指针的高级用法的理解。我在总结这篇文章时候都有点云里雾里,参考了网上很多老前辈的博客。如果本篇有什么错误,还请大家积极指正,谢谢大家。

2 源码解析

注:本篇代码取自kernel4.9

/*做初始化工作,代码路径:init/main.c  */
static void __init do_initcalls(void)
{
	int level;
	// 按照级别调用初始化函数,level表示级别,数字越小越先执行
	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
#ifdef CONFIG_MTK_RAM_CONSOLE
	aee_rr_rec_last_init_func(~(unsigned long)(0));
#endif
}

/*根据级别调用初始化函数,位置:init/main.c*/
static void __init do_initcall_level(int level)
{
	initcall_t *fn; // 这是一个函数指针后面会讲到
	// 复制命令行
	strcpy(initcall_command_line, saved_command_line);
	// 匹配参数,initcall_level_names是个数组,里面存放了各个级别的初始化函数级数名
	parse_args(initcall_level_names[level],
		   initcall_command_line, __start___param,
		   __stop___param - __start___param,
		   level, level,
		   NULL, &repair_env_string);
	// 执行各个初始化级别的函数
	for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
		do_one_initcall(*fn);
}

对于调用初始化函数,得先来了解以下代码

// 这段代码声明了initcall_t是一个返回值为int类型的函数指针
typedef int (*initcall_t)(void);   

下面这个宏用来替代下面各个级别宏代码中的__define_initcall(fn, id),而这个宏目的是组装一个完整的初始化函数,
比如以subsys_initcall为例,subsys_initcall(pci_subsys_init)宏展开后得到的东西是:

static initcall_t __initcall_pci_subsys_init4 __used __attribute__((__section__(".initcall.4.init")))
 = pci_subsys_init;
 LTO_REFERENCE_INITCALL(__initcall_pci_subsys_init4)

它定义了一个类型为initcall_t的函数,且该函数(就是__initcall_pci_subsys_init4)在编译时要放到section (TEXT SECTION)“.initcall.4.init"中去。

可见在宏INIT_CALLS中定义的这些section中放了一系列的函数,这些函数是用pure_initcallcore_initcall之类宏定义的,而且pure_initcall定义的initcall函数放在.initcall.0.init中,

core_initcall定义的initcall函数放在.initcall.1.init中,因此pure_initcall定义的函数要比core_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)
/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)		__define_initcall(fn, early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 * Keep main.c:initcall_level_names[] in sync.
 */
#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)

#define __exitcall(fn)						\
	static exitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn)					\
	static initcall_t __initcall_##fn			\
	__used __section(.con_initcall.init) = fn

#define security_initcall(fn)					\
	static initcall_t __initcall_##fn			\
	__used __section(.security_initcall.init) = fn

INIT_CALLS宏定义在以下代码,位置:include/asm-generic/vmlinux.lds.h

#define INIT_CALLS							\
		VMLINUX_SYMBOL(__initcall_start) = .;			\
		KEEP(*(.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) = .;

3 总结

目前我还并未完全搞懂这些原理,对于以上方式进行初始化函数调用,我认为是为了方便管理和统一初始化方式,毕竟初始化函数功能各异,存放位置也不一定,我也不知道对不对,希望有大佬能进行指正。

本系列链接传送:
【Android底层学习总结】1. 驱动开发基础
【Android底层学习总结】2. 安卓系统内核的Bring Up
【Android底层学习总结】3. 内核中driver_init函数源码解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值