该函数的实现体现了linux对第二类子系统进行初始化的方式的设计思想。Linux提供了不同的“初始化等级”,linux会按照等级从高(level 0)到低(level 7)来进行初始化,也就是说会首先初始化等级高的,然后初始化等级低的。其代码逻辑很简单:
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn
/*
* 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 early_initcall(fn) module_init(fn)
#define core_initcall(fn) module_init(fn)
#define postcore_initcall(fn) module_init(fn)
#define arch_initcall(fn) module_init(fn)
#define subsys_initcall(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn)
#define late_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)));
顺便提一句,module_init在不支持模块的系统中会变成:
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { \
INIT_DATA \
INIT_SETUP(initsetup_align) \
INIT_CALLS \
CON_INITCALL \
SECURITY_INITCALL \
INIT_RAM_FS \
}
该宏定义了初始化数据段,其中就包括了INIT_CALLS段。
#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) = .;
该宏即定义了各个初始化等级所在的section,比如INIT_CALLS_LEVEL(5)会被展开为:
同时从INIT_CALLS中也可以看出,所有等级的初始化函数都被放在从__initcall_start到__initall_end之间的内存区域中。
其它的内存区域的定义也可以在头文件“vmlinux.lds.h”中找到,如果感兴趣的话可以查看该文件。
Linux允许用户在启动时指定一些内核选项(也就是启动参数),这些参数会被传递给相应的处理函数。
有三种方式来定义内核选项:采用第一种方式定义的内核选项会在内核启动的开始阶段被parse_early_param处理。而其它两种方式则会由下边的调用处理:
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
parse_args的参数的含义:
module_param最终会通过下面的宏定义;
#define __module_param_call(prefix, name, ops, arg, perm, level) \
/* Default value instead of permissions? */ \
static int __param_perm_check_##name __attribute__((unused)) = \
BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \
+ BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN); \
static const char __param_str_##name[] = prefix #name; \
static struct kernel_param __moduleparam_const __param_##name \
__used \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
= { __param_str_##name, ops, perm, level, { arg } }
通过early_param和__setup定义的初始化参数会被放在.init.setup段中,具体的代码可以查看头文件"init.h"。.init.setup段的定义也在头文件“vmlinux.lds.h”中,如下所示
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
从上述内存段的定义可以看出,该宏定义的内存区域起始于__setup_start,结束于__setup_end。
__param : AT(ADDR(__param) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___param) = .; \
*(__param) \
VMLINUX_SYMBOL(__stop___param) = .; \
从上述内存段的定义可以看出,该宏定义的内存区域起始于__start___param,结束于__stop___param。
#define __exit __section(.exit.text) __exitused __cold notrace
如果一个函数加了个标记,则它会被放入.exit.text段,该段比较特殊,它被用于模块的退出函数,如果一个模块被编译到了内核映像中而不是以模块的形式编译的则该部分内存可以被链接器忽略,即不把它连接到内核中或者可以在系统启动后将它占用的内存给释放掉;同时如果内核不支持模块卸载,则也可以在链接时或者启动后释放该部分区域所占的内存。
下边这些宏直接给出内核代码中的注释,简单的加了这些标记的内存区域是可释放的(以节省内存)。
/* These are for everybody (although not all archs will actually
discard it in modules) */
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __constsection(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)
在内核启动完成后,它会启动第一个进程init进程。inittab是init进程的配置文件,该配置文件的格式如下:
id:runlevels:action:process 其中某些部分可以为空