kernel_init()
kernel_init() は rest_init() で生成されたカーネルスレッドにより実行され、PCI バスやファイルシステム、ネットワーク、その他カーネルに組み込まれているデバイスドライバの初期化やルートファイルシステムの作成等を行う。最後に init プログラムを起動し、init プロセスに変化する。
kernel_init() は init/main.c に定義されている。
static int __ref kernel_init(void *unused)
{
kernel_init_freeable();
...(中略)...
if (ramdisk_execute_command) {
if (!run_init_process(ramdisk_execute_command))
return 0;
pr_err("Failed to execute %s\n", ramdisk_execute_command);
}
まずは、kernel_init_freeable() が呼ばれる。kernel_init_freeable() は kernel_init() のすぐ下に定義されている。
以下、一度 kernel_init_freeable() の実装を追った後、再び kernel_init() に帰ってくることとする。
static noinline void __init kernel_init_freeable(void)
{
wait_for_completion(&kthreadd_done);
gfp_allowed_mask = __GFP_BITS_MASK;
set_mems_allowed(node_states[N_MEMORY]);
set_cpus_allowed_ptr(current, cpu_all_mask);
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
lockup_detector_init();
smp_init();
sched_init_smp();
do_basic_setup();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
pr_err("Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
load_default_modules();
}
- wait_for_completion(): 以降の処理で kthreadd を利用するため、rest_init() で kthreadd が起動するのを待つ処理
- set_cpus_allowed_ptr(): init プロセスが全ての CPU で実行できるようにマスクを設定する
- smp_prepare_cpus(): SMP の CPU 初期化を行う関数。この中で setuplocalAPIC() が呼ばれる
- sys_open((const char __user *) “/dev/console”, O_RDWR, 0): /dev/console を開く
最後に、ramdisk_execute_command に “/init” を指定する。これは initramfs を / にマウントし、 /init を実行するため。
それでは再び kernel_init() に話を戻す。
kernel_init_freeable() で ramdisk_execute_command の値がセットされたため、その値を用いて、run_init_process(ramdisk_execute_command) を実行する。
run_init_process() も同様に init/main.c で定義されている。
static int run_init_process(const char *init_filename)
{
argv_init[0] = init_filename;
return do_execve(getname_kernel(init_filename),
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
}
上記の do_execve() に “/init” が渡され、最終的に init プログラムが実行される流れとなる。