INIT_CALLS的秘密
在内核代码中经常会看到core_initcall(), subsys_initcall()这样xxx_initcall()的函数。
这些函数可以理解为c++中的构造函数,只是内核对这些函数做了分类,并且在特定的地方调用他们。
今天我们就来学习一下。
函数定义
先来看看都有哪些类似的函数:
#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)这些函数不仅有相似的名字,还有这相似的定义:
可以看到这些函数地址都保存在类型为initcall_t的变量中。而这个变量的定义上又加了对应的section属性。
那这个section的名字展开是什么呢?以early_initcall()为例,这个section展开后是 .initcallearly.init
函数链接
既然看到了section定义,那这个东西就要和链接脚本联系起来。
在x86上使用的脚本是arch/x86/kernel/vmlinux.lds.S,其中简化后长这样:
而INIT_DATA_SECTION的定义在文件include/asm-generic/vmlinux.lds.h中:
所以源代码中定义的所有函数都有各自对应的一个section,而且这么多函数都放在__initcall_start和__initcall_end指定的一段空间。
函数调用
好了,找了这么半天都还没有看到这些init函数究竟是在哪里被调用的。这又是一堆很狗血的东西,要不然为啥每次找都要花上一段时间呢。
还是直接写出调用的点吧,好像也没有什么可以多说的。
节奏上一共分成两个部分,第一部分只调用了__initcall_start到__initcall0_start之间的初始化函数。
而第二部分则是调用剩下的,要证实这点还要看initcall_levels的定义。
因为每一个level中包含多个定义好的函数,所以在do_initcall_level中还有一个循环调用每一个具体的函数。
Last updated
Was this helpful?