Linux Boot Sequence を読む ( 9 ) ~ kernel_init() ~

February 19, 2018

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 プログラムが実行される流れとなる。


 © 2023, Dealing with Ambiguity