插件窝 干货文章 Linux内核主函数的作用及执行过程解析

Linux内核主函数的作用及执行过程解析

初始化 init 内核 调度 1051    来源:    2025-04-08

Linux内核主函数的作用及执行过程解析

内核主函数概述

Linux内核的主函数是start_kernel(),它位于init/main.c文件中。这个函数是内核初始化过程的核心,负责初始化内核的所有主要子系统,并最终启动用户空间的第一个进程(通常是init进程)。

start_kernel()的主要作用

  1. 初始化内核核心子系统:包括调度器、内存管理、中断系统等
  2. 建立基本运行环境:设置CPU、内存、设备等基本硬件环境
  3. 初始化设备驱动:为后续设备驱动加载做好准备
  4. 挂载根文件系统:为系统提供持久化存储支持
  5. 启动用户空间:创建第一个用户进程(通常是/sbin/init)

执行过程详细解析

以下是start_kernel()函数的主要执行流程:

1. 早期初始化阶段

asmlinkage __visible void __init start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    // 设置当前任务(0号进程)的CPU掩码
    set_task_stack_end_magic(&init_task);

    // 初始化调试系统
    boot_init_stack_canary();

    // 初始化cgroup的早期子系统
    cgroup_init_early();

    // 关闭本地中断
    local_irq_disable();

    // 早期控制台初始化
    early_boot_irqs_disabled = true;
    boot_cpu_init();
    page_address_init();
    pr_notice("%s", linux_banner);
    setup_arch(&command_line);

    // 初始化内存管理
    mm_init_cpumask(&init_mm);
    setup_command_line(command_line);
    setup_nr_cpu_ids();
    setup_per_cpu_areas();
    smp_prepare_boot_cpu();

    // 构建所有CPU的调度域
    build_all_zonelists(NULL);
    page_alloc_init();
}

2. 核心子系统初始化

    // 打印命令行参数
    pr_notice("Kernel command line: %s\n", boot_command_line);

    // 解析命令行参数
    parse_early_param();
    after_dashes = parse_args("Booting kernel",
                  static_command_line, __start___param,
                  __stop___param - __start___param,
                  -1, -1, &unknown_bootoption);

    // 初始化跳转标签
    jump_label_init();

    // 设置日志缓冲区
    setup_log_buf(0);

    // 初始化PID哈希表
    pidhash_init();

    // 初始化虚拟文件系统(VFS)缓存
    vfs_caches_init_early();

    // 初始化排序机制
    sort_main_extable();

    // 初始化中断和陷阱
    trap_init();

    // 初始化内存管理
    mm_init();

    // 初始化调度器
    sched_init();

    // 初始化RCU机制
    rcu_init();

    // 初始化定时器
    tick_init();
    init_timers();
    hrtimers_init();
    softirq_init();
    timekeeping_init();
    time_init();

    // 初始化性能事件
    perf_event_init();

    // 初始化分析器
    profile_init();

    // 初始化控制台
    console_init();

    // 初始化锁调试
    lockdep_info();

    // 初始化内存保护键
    pkeys_init();

    // 初始化页表
    page_ext_init();

    // 初始化kmemleak
    kmemleak_init();

    // 初始化调试对象
    debug_objects_mem_init();

    // 初始化内核内存分配器
    setup_per_cpu_pageset();
    numa_policy_init();
    if (late_time_init)
        late_time_init();
    sched_clock_init();
    calibrate_delay();
    pidmap_init();
    anon_vma_init();
    acpi_early_init();
    thread_stack_cache_init();
    cred_init();
    fork_init();
    proc_caches_init();
    buffer_init();
    key_init();
    security_init();
    dbg_late_init();
    vfs_caches_init();
    pagecache_init();
    signals_init();
    proc_root_init();
    nsfs_init();
    cpuset_init();
    cgroup_init();
    taskstats_init_early();
    delayacct_init();
    check_bugs();
    acpi_subsystem_init();
    sfi_init_late();
    efi_enable_reset_attack_mitigation();
    ftrace_init();

    // 初始化设备驱动
    rest_init();
}

3. 最终启动阶段

start_kernel()的最后,会调用rest_init()函数,这是内核初始化的最后阶段:

static noinline void __init_refok rest_init(void)
{
    int pid;

    // 创建内核线程kernel_init(即init进程)
    kernel_thread(kernel_init, NULL, CLONE_FS);

    // 创建kthreadd内核线程
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

    // 设置kthreadd线程的CPU亲和性
    rcu_read_lock();
    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    rcu_read_unlock();

    // 完成系统启动
    complete(&kthreadd_done);

    // 初始化调度器空闲循环
    init_idle_bootup_task(current);

    // 调度器开始运行
    schedule_preempt_disabled();

    // CPU进入空闲状态
    cpu_startup_entry(CPUHP_ONLINE);
}

关键子系统初始化详解

1. 内存管理初始化

mm_init()函数负责初始化内存管理系统: - 初始化页表 - 建立内存区域(zone) - 初始化slab分配器 - 设置虚拟内存管理

2. 调度器初始化

sched_init()函数初始化进程调度系统: - 初始化运行队列 - 设置调度类 - 建立调度域 - 初始化负载均衡机制

3. 设备驱动初始化

设备驱动初始化分为多个阶段: 1. 早期控制台初始化(console_init()) 2. 核心驱动框架初始化 3. 平台特定驱动初始化 4. 设备树解析和驱动匹配

4. 文件系统初始化

vfs_caches_init()初始化虚拟文件系统: - 初始化inode缓存 - 初始化dentry缓存 - 注册默认文件系统 - 挂载根文件系统

从内核到用户空间的过渡

kernel_init()函数负责从内核空间过渡到用户空间: 1. 完成剩余的初始化工作 2. 尝试执行用户指定的init程序 3. 如果失败,尝试默认的init程序(/sbin/init, /etc/init等) 4. 如果所有init程序都失败,执行/bin/sh作为最后手段

总结

Linux内核的启动过程是一个复杂而有序的过程,start_kernel()作为主函数协调了所有子系统的初始化。理解这个过程对于深入掌握Linux内核工作原理、进行内核调试和性能优化都非常重要。每个子系统的初始化顺序都经过精心设计,确保依赖关系得到正确处理,最终为用户空间提供一个稳定可靠的运行环境。