Linux Boot Sequence を読む ( 8 ) ~ rest_init() ~

February 19, 2018

rest_init()

start_kernel() は最後に restinit() を実行してその生涯に幕を閉じる。
rest\
init() では、init プロセスを起動するためのかーねるスレッドとカーネルスレッドデーモンを生成し、その後に CPU の休止とスケジューラ呼び出しを永久に繰り返すルーチンを呼び出す。
コードは init/main.c で定義されている。

static noinline void __init_refok rest_init(void)  
{  
  
	...(中略)...  
  
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  
        numa_default_policy();  
        pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);  
	rcu_read_lock();  
        kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);  
        rcu_read_unlock();  
	complete(&kthreadd_done);  
  
	/*                                                                                                                                                                                 
         * The boot idle thread must execute schedule()                                                                                                                                    
         * at least once to get things moving:                                                                                                                                             
         */  
	init_idle_bootup_task(current);  
        schedule_preempt_disabled();  
	/* Call into cpu_idle with preempt disabled */  
        cpu_startup_entry(CPUHP_ONLINE);  
}  

kernel_thread() はカーネルスレッドを生成するサブルーチン (do_fork() して第一引数の関数ポインタを実行する) であり、kernel_thread(kernel_init, NULL, CLONE_FS | CLONESIGHAND) では kernel\init() を実行するカーネルスレッドが生成される。
このスレッドは、カーネルに組み込まれているモジュールの初期化処理やルートファイルシステムのマウントを行い、最後に init プログラムを起動して、init プロセスに変化する。
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES) では、kthreadd (カーネルスレッドデーモン) のスレッドが生成される。kthreadd はカーネルスレッドの生成処理を請け負うスレッドであり、init と kthreadd 以外のカーネルスレッドは全てこいつで生成される。

cpu_startup_entry()

rest_init() 内の cpu_startup_entry() で cpu_idle_loop() が non-preemption で呼ばれ、cpu_idle_loop() では以下のルーチンが永久に繰り返される。

  1. 実行可能状態のプロセスが現れるまで CPU を停止状態にする
  2. CPU の動作が再開されるとプロセス* スケジューラを呼び出して CPU を譲渡する
  3. その後実行可能なプロセスがなくなると cpu_idle_loop() に制御が戻るので、再び実行可能状態のプロセスが現れるまで CPU を停止状態にする

cpu_idle_loop() は kernel/cpu/idle.c で定義される。

static void cpu_idle_loop(void)  
{  
        while (1) {  
  
                __current_set_polling();  
                tick_nohz_idle_enter();  
  
                while (!need_resched()) {  
                        check_pgt_cache();  
                        rmb();  
  
                        if (cpu_is_offline(smp_processor_id()))  
                                arch_cpu_idle_dead();  
  
                        local_irq_disable();  
                        arch_cpu_idle_enter();  
  
                       if (cpu_idle_force_poll || tick_check_broadcast_expired()) {  
                                cpu_idle_poll();  
                        } else {  
                                if (!current_clr_polling_and_test()) {  
                                        stop_critical_timings();  
                                        rcu_idle_enter();  
                                        arch_cpu_idle();  
                                        WARN_ON_ONCE(irqs_disabled());  
                                        rcu_idle_exit();  
                                        start_critical_timings();  
                                } else {  
                                        local_irq_enable();  
                                }  
                                __current_set_polling();  
                        }  
                        arch_cpu_idle_exit();  
                }  
                tick_nohz_idle_exit();  
                __current_clr_polling();  
  
               smp_mb__after_atomic();  
  
                sched_ttwu_pending();  
                schedule_preempt_disabled();  
        }  
}  

 © 2023, Dealing with Ambiguity