Linux系统里,有些进程只有kernel部分的代码,即由一个kernel函数进入,在sched的时候,将其与用户进程同等对待。
PID为0的叫swapper或sched进程,对应函数为rest_init
init进程PID为1,
kthreadd进程PID为2,
0号进程,即rest_init主体如下
1 static noinline void __init_refok rest_init(void) 2 { 3 int pid; 4 5 rcu_scheduler_starting(); 6 smpboot_thread_init(); 7 /* 8 * We need to spawn init first so that it obtains pid 1, however 9 * the init task will end up wanting to create kthreads, which, if 10 * we schedule it before we create kthreadd, will OOPS. 11 */ 12 kernel_thread(kernel_init, NULL, CLONE_FS); 13 numa_default_policy(); 14 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); 15 rcu_read_lock(); 16 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); 17 rcu_read_unlock(); 18 complete(&kthreadd_done); 19 20 /* 21 * The boot idle thread must execute schedule() 22 * at least once to get things moving: 23 */ 24 init_idle_bootup_task(current); 25 schedule_preempt_disabled(); 26 /* Call into cpu_idle with preempt disabled */ 27 cpu_startup_entry(CPUHP_ONLINE);
先是rcu_scheduler_starting函数,这个函数的作用是
再是smpboot_thread_init函数,这个函数注册smpboot时候的notifier,响应CPU动作
1 static struct notifier_block smpboot_thread_notifier = { 2 .notifier_call = smpboot_thread_call, 3 .priority = CPU_PRI_SMPBOOT, 4 }; 5 6 void __cpuinit smpboot_thread_init(void) 7 { 8 register_cpu_notifier(&smpboot_thread_notifier); 9 }
然后调用kernel_thread函数fork init进程
1 /* 2 * Create a kernel thread. 3 */ 4 pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) 5 { 6 return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn, 7 (unsigned long)arg, NULL, NULL); 8 }
do_fork函数被调用
这里有一点要注意,init进程执行的时候要等kerneladd进程执行后才被complete(&kthreadd_done)唤醒,然后真正执行,不然直接调度到init的话会OOPs
numa_default_policy被调用设置内存numa默认策略
然后调用kernel_thread函数fork kthreadd进程
然后用find_task_by_pid_ns(pid, &init_pid_ns)获取kthreadd的task_struct
最后唤醒complete(&kthreadd_done)进程
最后,0号进程,即空闲进程,要让一切跑起来,调用一次schedule