【Linux kernel基础】arch_initcall到底是怎么是一回事【doing】

本文详细探讨了Linux内核初始化过程中的arch_initcall机制,通过分析`customize_machine`函数的调用路径,解释了arch_initcall宏如何将函数添加到初始化调用链中。同时,解释了相关宏的层次展开,如`__initcall_id`、`__initcall_stub`等,展示了它们在构建初始化函数名称和代码段名称中的作用。文章还提及了链接文件在调用这些初始化函数中的角色,并讨论了不同initcall类型的执行顺序。

问题背景

在学习【奔跑吧Linux内核——基于Linux 4.x内核源代码问题分析】中断管理机制章节中如下部分:
系统初始化时,customize_machine()函数会去枚举并初始化“arm,amba-bus”和
“simple-bus”总线上的设备,最终解析 DTS 中的相关信息,把相关信息添加到 struct device
数据结构中,向 Linux 内核注册一个新的外设。我们只关注中断相关信息的枚举过程:
[customize_machine()->of_platform_populate()->of_platform_bus_create()->of
_amba_device_create()]

查看customize_machine接口,看到arch_initcall,产生好奇,不知道其具体是做什么,及customize_machine在哪里被调到(在代码中直接找,没找到调用点)
machine_desc->init_machine和machine_desc是全局变量?待研究,TODO

[arch/arm/kernel/setup.c]
static int __init customize_machine(void)
{
	/*
	 * customizes platform devices, or adds new ones
	 * On DT based machines, we fall back to populating the
	 * machine from the device tree, if no callback is provided,
	 * otherwise we would always need an init_machine callback.
	 */
	if (machine_desc->init_machine)
		machine_desc->init_machine();

	return 0;
}
arch_initcall(customize_machine);

arch_initcall宏定义

代码中找到两处定义,为啥两处都有定义,看代码后应该是init.h
module.h里内容待后边研究,TODO
在这里插入图片描述

[include/linux/init.h]
/*
 * 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)


[include/linux/module.h]
/*
 * In most cases loadable modules do not need custom
 * initcall levels. There are still some valid cases where
 * a driver may be needed early if built in, and does not
 * matter when built as a loadable module. Like bus
 * snooping debug drivers.
 */
#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)

/* Each module must use one module_init(). */
#define module_init(initfn)					\
	static inline initcall_t __maybe_unused __inittest(void)		\
	{ return initfn; }					\
	int init_module(void) __copy(initfn)			\
		__attribute__((alias(#initfn)));		\
	__CFI_ADDRESSABLE(init_module, __initdata);

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)					\
	static inline exitcall_t __maybe_unused __exittest(void)		\
	{ return exitfn; }					\
	void cleanup_module(void) __copy(exitfn)		\
		__attribute__((alias(#exitfn)));		\
	__CFI_ADDRESSABLE(cleanup_module, __exitdata);

module.h里内容按下不表
先看init.h中的定义:

#define arch_initcall(fn)		__define_initcall(fn, 3)
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)

___define_initcall宏第三个参数.initcall##id:
查看该宏定义处,可知为__sec,即为代码段名称,最终展开为字符串.initcall3

#define ___define_initcall(fn, id, __sec)			\
	__unique_initcall(fn, id, __sec, __initcall_id(fn))

#define __unique_initcall(fn, id, __sec, __iid)			\
	____define_initcall(fn,					\
		__initcall_stub(fn, __iid, id),			\
		__initcall_name(initcall, __iid, id),		\
		__initcall_section(__sec, __iid))

__initcall_id(fn)宏

/*
 * initcalls are now grouped by functionality into separate
 * subsections. Ordering inside the subsections is determined
 * by link order. 
 * For backwards compatibility, initcall() puts the call in 
 * the device init subsection.
 *
 * The `id' arg to __define_initcall() is needed so that multiple initcalls
 * can point at the same handler without causing duplicate-symbol build errors.
 *
 * Initcalls are run by placing pointers in initcall sections that the
 * kernel iterates at runtime. The linker can do dead code / data elimination
 * and remove that completely, so the initcall sections have to be marked
 * as KEEP() in the linker script.
 */

/* Format: <modname>__<counter>_<line>_<fn> */
#define __initcall_id(fn)				\
	__PASTE(__KBUILD_MODNAME,			\
		__PASTE(__,						\
			__PASTE(__COUNTER__,		\
				__PASTE(_,				\
					__PASTE(__LINE__,	\
						__PASTE(_, fn)	\
					)					\
				)						\
			)							\
		)								\
	)									\

__initcall_id(fn)宏按层剥开:
在这里插入图片描述

再结合__PASTE宏(字符拼接)

/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)

__initcall_id(fn)宏小结:

__initcall_id(fn)宏展开是下边个鬼玩意:
在这里插入图片描述
当然上述宏展开后字符串中的三个宏,
在这里插入图片描述
编译器最终都会替换成对应的字符串
自己理解,整这么一长串,是为了防止重名

__unique_initcall(fn, id, __sec, __iid)宏

#define __unique_initcall(fn, id, __sec, __iid)					\
	____define_initcall(fn,										\
						__initcall_stub(fn, __iid, id),			\
						__initcall_name(initcall, __iid, id),	\
						__initcall_section(__sec, __iid)		\
	)															\

tip:宏参数__iid就是上一小节探究的__initcall_id(fn)宏

__initcall_name(prefix, __iid, id)宏

/* Format: __<prefix>__<iid><id> */
#define __initcall_name(prefix, __iid, id)			\
	__PASTE(__,						\
	__PASTE(prefix,						\
	__PASTE(__,						\
	__PASTE(__iid, id))))

结合该宏上层调用处,prefix(前缀)是:initcall字符
展开后:
prefix,,__iid,id
替换后
(真实字符串是需要去掉,的,此处为方便阅读保留)
initcall,,__KBUILD_MODNAME____COUNTER_____LINE___fn,id
替换id后:
在这里插入图片描述这是完整的初始化函数名称

__initcall_section(__sec, __iid)宏

#ifdef CONFIG_LTO_CLANG
#define __initcall_section(__sec, __iid)			\
	#__sec ".init.." #__iid
#define __initcall_stub(fn, __iid, id)				\
	__initcall_name(initstub, __iid, id)
#else
#define __initcall_section(__sec, __iid)			\
	#__sec ".init"
#define __initcall_stub(fn, __iid, id)	fn
#endif

由上文可知参数:
__sec是.initcall3,
__iid是__KBUILD_MODNAME____COUNTER_____LINE___fn,
展开后
.initcall3.init…__KBUILD_MODNAME____COUNTER_____LINE___fn,
或.initcall3.init
这是完整的代码段名称

__initcall_stub(fn, __iid, id)宏

#define __initcall_stub(fn, __iid, id)				\
	__initcall_name(initstub, __iid, id)

__initstub____KBUILD_MODNAME____COUNTER_____LINE___fn3
这是完整的初始化打桩函数名称

____define_initcall(fn, __unused, __name, __sec)宏

#define ____define_initcall(fn, __unused, __name, __sec)	\
	static initcall_t __name __used 			\
		__attribute__((__section__(__sec))) = fn;

把上边宏展开后:
其中__name是:
__initcall____KBUILD_MODNAME____COUNTER_____LINE___fn3

	static initcall_t __name __used 			\
		__attribute__((__section__(.initcall3.init))) = fn;

arch_initcall(fn)宏总结

arch_initcall(fn)-->fn: customize_machine
	__define_initcall(fn, id)-->id: 3
		___define_initcall(fn, id, __sec)-->__sec: .initcall3
			__unique_initcall(fn, id, __sec, __iid)-->__iid: __initcall_id(fn)
				____define_initcall(fn, __unused, __name, __sec)

调用处:
#define __unique_initcall(fn, id, __sec, __iid)					\
	____define_initcall(fn,										\
						__initcall_stub(fn, __iid, id),			\
						__initcall_name(initcall, __iid, id),	\
						__initcall_section(__sec, __iid)		\
	)
定义处:
#define ____define_initcall(fn, __unused, __name, __sec)	\
	static initcall_t __name __used 			\
		__attribute__((__section__(__sec))) = fn;

__iid: __initcall_id(fn)
	(__KBUILD_MODNAME)__(__COUNTER__)_(__LINE__)_fn
__unused: __initcall_stub(fn, __iid, id)(与编译宏CONFIG_LTO_CLANG是否定义有关)
__name: __initcall_name(initcall, __iid, id)
	__initcall__((__KBUILD_MODNAME)__(__COUNTER__)_(__LINE__)_fn)id

__sec:__initcall_section(__sec, __iid)
	.initcall3.init

__KBUILD_MODNAME:当前模块名字
__COUNTER__:出现次数?
__LINE__:行号

static initcall_t __name __used 			\
	__attribute__((__section__(.initcall3.init))) = fn;

下边需要查看链接文件,看该代码段在哪里被调用,TODO
待填坑。。。

用arch_initcall宏处理后的函数调用处



参考wiki:

各种initcall的执行先后顺序(module_init、postcore_initcall、arch_initcall、subsys_initcall、 fs_initcall)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值