启动第一个应用进程

start_kernel
  ......
  arch_call_rest_init()
  rest_init();
  pid = kernel_thread(kernel_init, NULL, CLONE_FS);

static int __ref kernel_init(void *unused)
{
    int ret;

    kernel_init_freeable();
    /* need to finish all async __init code before freeing the memory */
    async_synchronize_full();
    ftrace_free_init_mem();
    free_initmem();
    mark_readonly();

    /*
     * Kernel mappings are now finalized - update the userspace page-table
     * to finalize PTI.
     */
    pti_finalize();

    system_state = SYSTEM_RUNNING;
    numa_default_policy();

    rcu_end_inkernel_boot();
创建1号进程的3种方式,如下:
1.方式①
    if (ramdisk_execute_command) {
        ret = run_init_process(ramdisk_execute_command);
        if (!ret)
            return 0;
        pr_err("Failed to execute 
               ramdisk_execute_command, ret);
    }

    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
2.方式②
    if (execute_command) {
        ret = run_init_process(execute_command);
        if (!ret)
            return 0;
        panic("Requested init 
              execute_command, ret);
}
3.方式③
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;

    panic("No working init found.  Try passing init= option to kernel. "
          "See Linux Documentation/admin-guide/init.rst for guidance.");
}

从上述代码种可知,启动1号进程init一共有3种方式,分别是ramdisk的方式、execute_command的方式、默认方式。第一种是基于ramdisk的方式,将ram作为启动盘挂载根文件系统。第二种通常用于用户指定第一个init进程启动在根文件系统的位置。如果第一个和第二都没有就会使用默认。

ramdisk方式

static int __init rdinit_setup(char *str)
{
    unsigned int i;

    ramdisk_execute_command = str;
    /* See "auto" comment in init_setup */
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("rdinit=", rdinit_setup);

__setup(str,fn)的作用是,在系统开机时解析cmdline中是否有str字段,如果有就会调用fn函数,因此上面的函数意思就是当cmdline中有rdinit=字段时,就会调用rdinit_setup函数,将rdinit=后面的内容赋值给ramdisk_execute_command。

execute_command方式

static int __init init_setup(char *str)
{
    unsigned int i;

    execute_command = str;
    /*
     * In case LILO is going to boot us with default command line,
     * it prepends "auto" before the whole cmdline which makes
     * the shell think it should execute a script with such name.
     * So we ignore all arguments entered _before_ init=... [MJ]
     */
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("init=", init_setup);

上述代码同理,当cmdline中存在\"init=\"时,调用init_setup,将参数赋值给execute_command。这种情况一般是可以让用户指定启动的第一个进程。

默认方式

static int __ref kernel_init(void *unused)
{
   ......
   if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;
   ......
}

当cmdline中既没有rdinit和init字段时,就是直接使用默认的方式。