init/main.c
start_kernel->arch_call_rest_init->rest_init
负责完成系统初始化的最后几个重要步骤,主要包括初始化用户态进程(init进程)、创建内核线程管理器(kthreadd)以及启动CPU的空闲循环。
c
683 noinline void __ref rest_init(void)
684 {
685 struct task_struct *tsk;
686 int pid;
687
688 rcu_scheduler_starting();
689 /*
690 * We need to spawn init first so that it obtains pid 1, however
691 * the init task will end up wanting to create kthreads, which, if
692 * we schedule it before we create kthreadd, will OOPS.
693 */
// 创建用户模式线程,即系统初始化进程(通常称为init进程),使用kernel_init作为入口点,并设置了CLONE_FS标志来共享文件系统命名空间。这一步确保init进程获得PID 1
694 pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
695 /*
696 * Pin init on the boot CPU. Task migration is not properly working
697 * until sched_init_smp() has been run. It will set the allowed
698 * CPUs for init to the non isolated CPUs.
699 */
// 695至704:这部分代码将init进程固定在启动CPU上。由于此时任务调度尚未完全初始化,需要手动设置其CPU亲和性,避免调度问题。
700 rcu_read_lock();
701 tsk = find_task_by_pid_ns(pid, &init_pid_ns);
702 tsk->flags |= PF_NO_SETAFFINITY;
703 set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));
704 rcu_read_unlock();
705
706 numa_default_policy();
// 创建内核线程kthreadd,它是所有内核线程的父进程,负责管理和调度内核线程。同样使用了CLONE_FS和CLONE_FILES标志。
707 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
708 rcu_read_lock();
// 找到并记录刚创建的kthreadd线程的task_struct,以便后续使用.
709 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
710 rcu_read_unlock();
711
712 /*
713 * Enable might_sleep() and smp_processor_id() checks.
714 * They cannot be enabled earlier because with CONFIG_PREEMPTION=y
715 * kernel_thread() would trigger might_sleep() splats. With
716 * CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled
717 * already, but it's stuck on the kthreadd_done completion.
718 */
// 更新系统状态,表明系统已准备好进行进程调度。
719 system_state = SYSTEM_SCHEDULING;
720 // 表示kthreadd初始化完成,可以唤醒等待此事件的其他部分代码继续执行。比如上面创建的kernel_init线程,就是所谓的init进程
721 complete(&kthreadd_done);
722
723 /*
724 * The boot idle thread must execute schedule()
725 * at least once to get things moving:
726 */
727 schedule_preempt_disabled();
728 /* Call into cpu_idle with preempt disabled */
// 调用cpu_startup_entry启动CPU的空闲循环处理程序,这是CPU进入正常工作状态的最后一环,确保系统能够处理后续的任务调度。
729 cpu_startup_entry(CPUHP_ONLINE);
730 }
init负责所有用户空间进程创建,kthreadd是所有内核线程的祖先。
下面看kernel_init就是init进程:
c
1510 static int __ref kernel_init(void *unused)
1511 {
1512 int ret;
1513
1514 /*
1515 * Wait until kthreadd is all set-up.
1516 */
1517 wait_for_completion(&kthreadd_done);
1518
1519 kernel_init_freeable();
1520 /* need to finish all async __init code before freeing the memory */
1521 async_synchronize_full();
1522
1523 system_state = SYSTEM_FREEING_INITMEM;
1524 kprobe_free_init_mem();
1525 ftrace_free_init_mem();
1526 kgdb_free_init_mem();
1527 exit_boot_config();
1528 free_initmem();
1529 mark_readonly();
1530
1531 /*
1532 * Kernel mappings are now finalized - update the userspace page-table
1533 * to finalize PTI.
1534 */
1535 pti_finalize();
1536
1537 system_state = SYSTEM_RUNNING;
1538 numa_default_policy();
1539
1540 rcu_end_inkernel_boot();
1541
1542 do_sysctl_args();
1543
1544 pr_err("x5_test_init:%s:%d:ramdisk_execute_command=%s\n", __func__, __LINE__, ramdisk_execute_command);
1545 if (ramdisk_execute_command) {
1546 ret = run_init_process(ramdisk_execute_command);
1547 if (!ret)
1548 return 0;
1549 pr_err("Failed to execute %s (error %d)\n",
1550 ramdisk_execute_command, ret);
1551 }
1552
1553 /*
1554 * We try each of these until one succeeds.
1555 *
1556 * The Bourne shell can be used instead of init if we are
1557 * trying to recover a really broken machine.
1558 */
1559 if (execute_command) {
1560 ret = run_init_process(execute_command);
1561 if (!ret)
1562 return 0;
1563 panic("Requested init %s failed (error %d).",
1564 execute_command, ret);
1565 }
1566
1567 if (CONFIG_DEFAULT_INIT[0] != '\0') {
1568 ret = run_init_process(CONFIG_DEFAULT_INIT);
1569 if (ret)
1570 pr_err("Default init %s failed (error %d)\n",
1571 CONFIG_DEFAULT_INIT, ret);
1572 else
1573 return 0;
1574 }
1575
1576 if (!try_to_run_init_process("/sbin/init") ||
1577 !try_to_run_init_process("/etc/init") ||
1578 !try_to_run_init_process("/bin/init") ||
1579 !try_to_run_init_process("/bin/sh"))
1580 return 0;
1581
1582 panic("No working init found. Try passing init= option to kernel. "
1583 "See Linux Documentation/admin-guide/init.rst for guidance.");
1584 }
init上承kernel,下起用户空间进程,配置了整个用户空间工作环境。
kernel_init执行"/sbin/init",在此程序中,会执行到busybox的init进程入口,init_main.
在init_main()函数中会调用parse_inittab(void)函数,parse_inittab(void)函数可以使用一些默认的配置,当/etc/inittab没有配置时。
parse_inittab 里面会调用 new_init_action(SYSINIT, INIT_SCRIPT, ""),决定了接下去初始化的脚本是INIT_SCRIPT所定义的值。这个宏的默认值是"/etc/init.d/rcS"。
如此就执行到了rcS初始化脚本