内核启动过程
1、内核启动过程是操作系统启动的第一步,也是整个操作系统的基础。在Linux内核中,内核启动的入口是arch/x86/boot/header.S文件中的startup_32函数,该函数是内核的启动代码入口点。
2、在启动过程中,内核首先会执行启动代码,进行一些基本的初始化操作,包括设置中断向量表、初始化全局描述符表(GDT)和中断描述符表(IDT)、建立内核页表等。然后,内核会在arch/x86/kernel/head_32.S文件中的startup_32函数中进行一些硬件初始化操作,如初始化CPU,设置中断处理程序,初始化内存控制器等。
3、接下来,内核会进入C语言代码,执行start_kernel函数,该函数是内核的主函数,负责初始化各个子系统和驱动程序。在start_kernel函数中,内核会完成以下操作:
初始化内核参数和全局变量:
初始化参数和全局变量:在内核启动过程中,会初始化一些内核参数和全局变量,例如内核命令行参数、系统时间、CPU信息、内存信息等。这些参数和变量的初始化是在内核启动代码中完成的。
初始化系统调用和中断处理程序
初始化系统调用和中断处理程序:在内核启动过程中,会初始化系统调用和中断处理程序。系统调用是用户空间程序和内核之间进行交互的接口,中断处理程序是用于处理硬件中断的代码。系统调用和中断处理程序的初始化是在内核启动代码中完成的。
初始化虚拟文件系统(VFS),包括注册文件系统和创建根文件系统
初始化虚拟文件系统(VFS):在内核启动过程中,会初始化VFS,包括注册文件系统和创建根文件系统。VFS是Linux内核中的一个重要子系统,用于管理文件系统和文件操作。VFS的初始化是在内核启动代码中完成的。
初始化进程管理子系统,包括创建init进程和空闲进程
初始化进程管理子系统:在内核启动过程中,会初始化进程管理子系统,包括创建init进程和空闲进程。进程管理子系统是Linux内核中的一个重要子系统,用于管理进程和线程。进程管理子系统的初始化是在内核启动代码中完成的。
初始化内存管理子系统,包括建立内存映射表、初始化物理内存管理器等
初始化内存管理子系统:在内核启动过程中,会初始化内存管理子系统,包括建立内存映射表、初始化物理内存管理器等。内存管理子系统是Linux内核中的一个重要子系统,用于管理内存。内存管理子系统的初始化是在内核启动代码中完成的。
初始化设备驱动子系统,包括注册设备驱动程序、初始化设备等
初始化设备驱动子系统:在内核启动过程中,会初始化设备驱动子系统,包括注册设备驱动程序、初始化设备等。设备驱动子系统是Linux内核中的一个重要子系统,用于管理设备驱动程序和设备。设备驱动子系统的初始化是在内核启动代码中完成的。
初始化网络子系统,包括初始化网络设备、协议栈等
初始化网络子系统:在内核启动过程中,会初始化网络子系统,包括初始化网络设备、协议栈等。网络子系统是Linux内核中的一个重要子系统,用于管理网络设备和协议栈。网络子系统的初始化是在内核启动代码中完成的
启动用户空间进程,如init进程
启动用户空间进程:在内核启动过程中,会启动用户空间进程,如init进程。init进程是Linux系统中的第一个进程,负责启动其他进程和服务。启动用户空间进程的过程是在进程管理子系统的初始化函数中完成的
在完成以上操作后,内核就可以进入正常的运行状态,等待用户的操作。需要注意的是,内核启动过程是一个复杂的过程,其中涉及到很多细节问题和硬件相关的操作,需要有一定的操作系统和计算机体系结构的基础知识才能深入理解。
入口代码解读
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
char *command_line;
char *after_dashes;
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
init_vmlinux_build_id();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them.
*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
early_security_init();
setup_arch(&command_line);
setup_boot_config();
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
boot_cpu_hotplug_init();
build_all_zonelists(NULL);
page_alloc_init();
pr_notice("Kernel command line: %s\n", saved_command_line);
/* parameters may set static keys */
jump_label_init();
parse_early_param();
after_dashes = parse_args("Booting kernel",
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, NULL, &unknown_bootoption);
print_unknown_bootoptions();
if (!IS_ERR_OR_NULL(after_dashes))
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
NULL, set_init_arg);
if (extra_init_args)
parse_args("Setting extra init args", extra_init_args,
NULL, 0, -1, -1, NULL, set_init_arg);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
ftrace_init();
/* trace_printk can be enabled here */
early_trace_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
radix_tree_init();
/*
* Set up housekeeping before setting up workqueues to allow the unbound
* workqueue to take non-housekeeping into account.
*/
housekeeping_init();
/*
* Allow workqueue creation and work item queueing/cancelling
* early. Work item execution depends on kthreads and starts after
* workqueue_init().
*/
workqueue_init_early();
rcu_init();
/* Trace events are available after this */
trace_init();
if (initcall_debug)
initcall_debug_enable();
context_tracking_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
rcu_init_nohz();
init_timers();
srcu_init();
hrtimers_init();
softirq_init();
timekeeping_init();
kfence_init();
/*
* For best initial stack canary entropy, prepare it after:
* - setup_arch() for any UEFI RNG entropy and boot cmdline access
* - timekeeping_init() for ktime entropy used in rand_initialize()
* - rand_initialize() to get any arch-specific entropy like RDRAND
* - add_latent_entropy() to get any latent entropy
* - adding command line entropy
*/
rand_initialize();
add_latent_entropy();
add_device_randomness(command_line, strlen(command_line));
boot_init_stack_canary();
time_init();
perf_event_init();
profile_init();
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();
kmem_cache_init_late();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
if (panic_later)
panic("Too many boot %s vars at `%s'", panic_later,
panic_param);
lockdep_init();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
locking_selftest();
/*
* This needs to be called before any devices perform DMA
* operations that might use the SWIOTLB bounce buffers. It will
* mark the bounce buffers as decrypted so that their usage will
* not cause "plain-text" data to be decrypted when accessed.
*/
mem_encrypt_init();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
setup_per_cpu_pageset();
numa_policy_init();
acpi_early_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pid_idr_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_stack_cache_init();
cred_init();
fork_init();
proc_caches_init();
uts_ns_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init();
pagecache_init();
signals_init();
seq_file_init();
proc_root_init();
nsfs_init();
cpuset_init();
cgroup_init();
taskstats_init_early();
delayacct_init();
poking_init();
check_bugs();
acpi_subsystem_init();
arch_post_acpi_subsys_init();
kcsan_init();
/* Do the rest non-__init'ed, we're now alive */
arch_call_rest_init();
prevent_tail_call_optimization();
}
1. `set_task_stack_end_magic(&init_task)`:设置任务栈结束的魔数。
2. `smp_setup_processor_id()`:设置处理器ID。
3. `debug_objects_early_init()`:初始化调试对象,用于调试内核代码。
4. `init_vmlinux_build_id()`:初始化内核版本号。
5. `cgroup_init_early()`:初始化控制组子系统。
6. `local_irq_disable()`:禁止本地中断。
7. `early_boot_irqs_disabled = true`:设置早期启动中断为禁用状态。
8. `boot_cpu_init()`:初始化启动CPU。
9. `page_address_init()`:初始化页面地址。
10. `pr_notice("%s", linux_banner)`:打印内核版本信息。
11. `early_security_init()`:初始化安全子系统。
12. `setup_arch(&command_line)`:设置架构。
13. `setup_boot_config()`:设置引导配置。
14. `setup_command_line(command_line)`:设置命令行参数。
15. `setup_nr_cpu_ids()`:设置CPU数量。
16. `setup_per_cpu_areas()`:设置每个CPU的私有数据。
17. `smp_prepare_boot_cpu()`:为启动CPU准备SMP。
18. `boot_cpu_hotplug_init()`:初始化热插拔CPU。
19. `build_all_zonelists(NULL)`:构建内存区域列表。
20. `page_alloc_init()`:初始化页面分配器。
21. `pr_notice("Kernel command line: %s\n", saved_command_line)`:打印内核命令行。
22. `jump_label_init()`:初始化跳转标签。
23. `parse_early_param()`:解析早期参数。
24. `after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, NULL, &unknown_bootoption)`:解析命令行参数。
25. `print_unknown_bootoptions()`:打印未知的引导选项。
26. `parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg)`:解析命令行参数。
27. `parse_args("Setting extra init args", extra_init_args, NULL, 0, -1, -1, NULL, set_init_arg)`:解析额外的命令行参数。
28. `setup_log_buf(0)`:设置日志缓冲区。
29. `vfs_caches_init_early()`:初始化VFS缓存。
30. `sort_main_extable()`:排序异常表格。
31. `trap_init()`:初始化陷阱处理程序。
32. `mm_init()`:初始化内存管理器。
33. `ftrace_init()`:初始化函数跟踪。
34. `early_trace_init()`:初始化跟踪日志。
35. `sched_init()`:初始化调度器。
36. `radix_tree_init()`:初始化基数树。
37. `housekeeping_init()`:初始化系统维护。
38. `workqueue_init_early()`:初始化工作队列。
39. `rcu_init()`:初始化RCU。
40. `trace_init()`:初始化跟踪事件。
41. `initcall_debug_enable()`:启用初始化调试。
42. `context_tracking_init()`:初始化上下文跟踪。
43. `early_irq_init()`:初始化早期中断。
44. `init_IRQ()`:初始化中断处理程序。
45. `tick_init()`:初始化定时器中断。
46. `rcu_init_nohz()`:初始化RCU中的无中断模式。
47. `init_timers()`:初始化定时器。
48. `srcu_init()`:初始化SRCU。