3.7.2.3. kernel_init

0号进程创建1号进程的方式如下

kernel_thread(kernel_init, NULL, CLONE_FS);

1号进程的执行函数就是 kernel_init ,这个函数定义在 init/main.c 中,代码实现如下

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();

    if (ramdisk_execute_command) {
        ret = run_init_process(ramdisk_execute_command);
        if (!ret)
            return 0;
        pr_err("Failed to execute %s (error %d)\n",
               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.
     */
    if (execute_command) {
        ret = run_init_process(execute_command);
        if (!ret)
            return 0;
        panic("Requested init %s failed (error %d).",
              execute_command, ret);
    }
    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.");
}

kernel_init 函数执行内核的部分初始化工作,及系统配置,并创建若干个用于高速缓存和虚拟内存管理的内核线程

3.7.2.3.1. init进程

1号进程调用do_execve运行可执行程序init,并演变为用户态1号进程,即init进程。init进程有许多重要的任务,比如启动getty(用于用户登录)、实现运行级别、以及处理孤立进程

0号进程—->1号内核进程—->1号用户进程—->getty进程—->shell进程

内核会在几个位置来查询init,按优先级顺序依次为 1. /sbin/init 2. /etc/init 3./bin/init 4/bin/sh

init程序是一个可以由用户编写的进程,以下是init的几个变种

init包

说明

sysvinit

早期使用的初始化进程工具,目前逐渐淡出linux的历史舞台

upstart

debian,ubuntu等系统使用的init daemon

systemd

systemd是linux中最新的初始化系统(init),可以提高系统的启动速度

3.7.2.3.2. kerner_init分析

执行流程

说明

kernel_init_freeable

完成初始化工作,准备文件系统,准备模块信息

async_synchronize_full

用来加速linux kernel开机的效率

free_initmem

free_initmem(in arch/arm/mm/init.c)释放kernel介于__init_begin到__init_end属于init Section的函数的所有内存,并把page个数加到 tatolram_pages中,作为后续kernel在配置mem时可以使用的pages

system_state

设置运行状态为SYSTEM_RUNNING

加载init进程

a.如果ramdisk_execute_command不为0就执行该命令为init user process b.如果execute_command不为0,就行执行该命令称为init user process c.依序执行/sbin/init /etc/init /bin/init /bin/sh

3.7.2.3.3. kernel_init_freeable流程分析

static noinline void __init kernel_init_freeable(void)
{
    /*
     * Wait until kthreadd is all set-up.
     */
    wait_for_completion(&kthreadd_done);
    //等待kernel thread kthreadd(pid=2)创建完毕

    /* Now the scheduler is fully set up and can do blocking allocations */
    gfp_allowed_mask = __GFP_BITS_MASK;
    //设置bitmask,使得init进程可以使用PM并且运行I/O阻塞操作


    /*
     * init can allocate pages on any node
     */
    set_mems_allowed(node_states[N_MEMORY]);
    //分配物理页面

    cad_pid = task_pid(current);
    //设置目前运行进程init的pid号到cad_pid(cad_pid用来接收ctrl-alt-del reboot signal的进程,如果设置cad=1表示可以处理来自ctl-alt-del的动作),
    //最后会调用ctrl_alt_del(void)函数并确认cad是否为1确定后将执行cad_work=deferred_cad,执行kernel_restart

    smp_prepare_cpus(setup_max_cpus);
    //体系结构相关的函数,实例在arch/arm64/kernel/smp.c中,调用smp_prepare_cpus时会以全局的setup_max_cpus为参数表示在编译核心时设定的CPU最大数量

    workqueue_init();
    //工作者队列初始化

    init_mm_internals();

    do_pre_smp_initcalls();
    //实例在init/main.c中,会透过函数do_one_initcall执行symbol中__initcall_start与__initcall0_start之间的函数
    lockup_detector_init();

    smp_init();
    //实例在kernel/smp.c中,函数主要是bootstrap处理器,运行active多核心架构下的其他处理器
    sched_init_smp();

    page_alloc_init_late();
    /* Initialize page ext after all struct pages are initialized. */
    page_ext_init();

    do_basic_setup();
    //实例在init/main.c中

    /* Open the /dev/console on the rootfs, this should never fail */
    if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        pr_err("Warning: unable to open an initial console.\n");

    (void) ksys_dup(0);
    (void) ksys_dup(0);
    /*
     * check if there is an early userspace init.  If yes, let it do all
     * the work
     */

    if (!ramdisk_execute_command)
        ramdisk_execute_command = "/init";

    if (ksys_access((const char __user *)
            ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }

    /*
     * Ok, we have completed the initial bootup, and
     * we're essentially up and running. Get rid of the
     * initmem segments and start the user-mode stuff..
     *
     * rootfs is available now, try loading the public keys
     * and default modules
     */

    integrity_load_keys();
}
/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)
{
    cpuset_init_smp();
    driver_init();
    //函数实现位于drivers/base/init.c中,初始化linux driver system model
    init_irq_proc();
    //初始化/proc/irq与其下的file node
    do_ctors();
    // init/main.c中实现,执行位于__ctors_start和__ctors_end中得代码段
    usermodehelper_enable();
    //kernel/kmod.c中实现,产生khelper workqueue
    do_initcalls();
}
static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}
//initcall函数调用,编译到内核中驱动在此函数中加载