从 Linux 3.10.1 start_kernel() 开始
总体上,start_kernel() 函数负责初始化和启动内核的各个子系统,创建第一个用户进程,以及进入内核的主循环,从而实现整个内核的启动过程。不同的内核版本和配置可能会有一些细微的差异,但一般都会遵循类似的基本流程。
asmlinkage void __init start_kernel(void) 这行代码各个关键字是什么意思
这行代码是Linux内核的入口函数,用于启动内核的初始化过程。下面是对各个关键字的解释:
asmlinkage:是一个宏定义,用于告诉编译器将函数的参数传递方式设置为使用堆栈传递,而不是使用寄存器传递。这是为了与用户空间的函数调用兼容。
void:表示该函数没有返回值。
__init:是一个宏定义,用于告诉编译器将该函数标记为内核初始化函数。这意味着该函数只会在内核初始化阶段被调用,并且在初始化完成后会被释放,以节省内存空间。
start_kernel:是函数的名称,表示这是内核的入口函数。
(void):表示该函数没有参数。
综上所述,这行代码定义了一个没有返回值、没有参数、被标记为内核初始化函数的入口函数。
start_kernel() 在什么时候被执行
start_kernel()函数是Linux内核的入口函数,它在内核启动的最后阶段被执行。具体来说,当引导加载程序(如GRUB)将控制权转交给内核时,start_kernel()函数被调用。在这个函数中,内核会完成一系列的初始化工作,包括设置中断向量表、初始化内存管理、初始化进程调度器、初始化设备驱动等。最后,start_kernel()函数会调用rest_init()函数,进入内核的主循环,等待系统的进一步操作。
代码1
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
代码说明:
command_line :一个指向字符的指针,用于存储命令行参数。
__start___param[]和__stop___param[] :两个数组,用于存储内核参数的起始地址和结束地址。这两个数组是由内核提供的,用于管理内核参数。
lockdep_init(): 初始化锁依赖性追踪系统,用于检测和调试多线程代码中的锁问题。
smp_setup_processor_id(): 设置每个 CPU 核心的唯一标识符,以便内核可以识别和管理多处理器系统中的不同核心。
debug_objects_early_init(): 初始化调试对象系统,用于跟踪和分析内核数据结构的使用情况。
boot_init_stack_canary(): 设置初始的栈金丝雀值,用于检测栈溢出漏洞。
cgroup_init_early(): 在早期阶段初始化控制组 (cgroup) 子系统,用于管理和限制进程组的资源使用。
local_irq_disable(): 禁用本地中断,以确保在早期启动期间不会发生中断处理。
early_boot_irqs_disabled = true;: 设置一个全局变量,表示早期启动阶段中断已被禁用。
这些操作主要是为了在系统启动的早期阶段进行一些必要的初始化和配置,以确保后续的启动过程和功能能够正常运行。
代码2
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
build_all_zonelists(NULL, NULL);
page_alloc_init();
pr_notice("Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
jump_label_init();
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
代码注释:
boot_cpu_init(): 初始化引导 CPU,执行与特定架构相关的引导 CPU 钩子。
page_address_init(): 初始化页表,为内核提供虚拟内存地址映射。
pr_notice("%s", linux_banner): 打印内核版本号和横幅信息。
setup_arch(&command_line): 执行特定架构的初始化操作,包括设置硬件和架构相关的功能。
mm_init_owner(&init_mm, &init_task): 初始化内存管理子系统的主要数据结构,包括初始化内存描述符和进程描述符。
mm_init_cpumask(&init_mm): 初始化内存管理子系统的 CPU 控制器,用于跟踪和管理每个 CPU 核心的内存分配情况。
setup_command_line(command_line): 设置内核命令行参数。
setup_nr_cpu_ids(): 设置 CPU 的数量和编号。
setup_per_cpu_areas(): 为每个 CPU 核心设置独立的内存区域,用于存储特定 CPU 的数据。
smp_prepare_boot_cpu(): 准备引导 CPU,执行与特定架构相关的引导 CPU 钩子。
build_all_zonelists(NULL, NULL): 构建内存区域链表,用于管理物理和虚拟内存的分配和释放。
page_alloc_init(): 初始化页面分配和管理子系统。
pr_notice("Kernel command line: %s\n", boot_command_line): 打印内核命令行参数。
parse_early_param(): 解析并处理早期内核参数。
parse_args(...): 解析并处理内核命令行参数。
jump_label_init(): 初始化跳转标签机制,用于优化内核代码的执行。
setup_log_buf(0): 设置内核日志缓冲区,用于记录内核日志信息。
pidhash_init(): 初始化进程 ID 哈希表,用于管理进程的唯一标识符。
vfs_caches_init_early(): 初始化虚拟文件系统的缓存,用于提高文件操作的性能。
sort_main_extable(): 对内核的异常处理表进行排序,以提高异常处理的效率。
trap_init(): 初始化异常和中断处理机制。
mm_init(): 初始化内存管理子系统的其他组件,包括内存映射和内存分配器。
这些操作进一步设置和初始化内核的各个子系统,为后续的启动和执行过程做准备。完成这些操作后,内核会启用中断,并继续执行后续的初始化和配置。
代码3
/*
* 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();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
idr_init_cache();
perf_event_init();
rcu_init();
tick_nohz_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_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(panic_later, panic_param);
lockdep_info();
/*
* 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();
代码说明
这段代码是 Linux 内核启动过程的继续部分。在这里,一些必要的初始化和配置操作被执行,并且中断被启用。
preempt_disable(): 禁用内核的抢占调度,以确保在早期启动期间不会发生抢占。
if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n")) local_irq_disable(): 检测中断是否在非常早期启用,并在必要时禁用中断。
idr_init_cache(): 初始化全局 IDR (ID Register) 缓存,用于管理全局 ID 对象的分配和释放。
perf_event_init(): 初始化性能事件子系统,用于监视和分析系统的性能指标。
rcu_init(): 初始化读-复制-更新 (RCU) 机制,用于实现无锁数据访问。
tick_nohz_init(): 初始化节拍器的非高精度定时器模式,以减少系统节拍对功耗的影响。
radix_tree_init(): 初始化基数树数据结构,用于高效地存储和检索键值对。
early_irq_init(): 在初始化 ISA IRQ (中断请求) 之前,初始化一些相关的链接和数据结构。
init_IRQ(): 初始化中断请求子系统,设置中断处理程序和处理中断的机制。
tick_init(): 初始化节拍器,用于计算和跟踪系统的节拍。
init_timers(): 初始化定时器子系统,用于创建和管理内核中的各种定时器。
hrtimers_init(): 初始化高精度定时器子系统,用于实现纳秒级别的计时。
softirq_init(): 初始化软中断机制,用于异步地执行延迟的工作。
timekeeping_init(): 初始化时间管理子系统,包括实时时钟和计时周期的设置。
time_init(): 初始化系统时间和时钟源。
profile_init(): 初始化性能分析子系统,用于收集和分析系统的性能数据。
call_function_init(): 初始化异步调用子系统,用于在多个 CPU 核心之间进行函数调用。
WARN(!irqs_disabled(), "Interrupts were enabled early\n"): 检测中断是否在早期启用,并在必要时发出警告。
early_boot_irqs_disabled = false;: 设置一个全局变量,表示早期启动阶段中断已被启用。
local_irq_enable(): 启用本地中断,从此开始响应中断。
kmem_cache_init_late(): 在较晚的阶段初始化内核内存缓存,用于分配和管理内核对象的内存。
console_init(): 初始化控制台设备,使其在启动过程中能够输出信息。
if (panic_later) panic(panic_later, panic_param): 如果发生了早期的致命错误,触发内核恐慌。
lockdep_info(): 初始化锁依赖性分析工具,用于检测和调试锁相关的问题。
locking_selftest(): 在中断启用后运行自检程序,以测试中断禁用和启用的正确性。
这些操作继续设置和初始化内核的其他子系统和功能,并确保中断被正确地启用。
代码4
#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
page_cgroup_init();
debug_objects_mem_init();
kmemleak_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();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
efi_late_init();
efi_free_boot_services();
}
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init();
代码说明
if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { ... }: 如果 initrd 内存镜像的起始地址低于最小可用物理页帧号,将禁用 initrd。
page_cgroup_init(): 初始化页面控制组,用于跟踪和管理页面的使用情况。
debug_objects_mem_init(): 初始化调试对象系统的内存部分,用于跟踪和分析内存对象的使用情况。
kmemleak_init(): 初始化内存泄漏检测工具,用于检测和调试内存泄漏问题。
setup_per_cpu_pageset(): 设置每个 CPU 核心的页面集合,用于跟踪和管理每个核心的页面分配情况。
numa_policy_init(): 初始化 NUMA (非一致性内存访问) 策略,用于管理多个节点的内存分配和访问。
if (late_time_init) late_time_init(): 如果存在延迟时间初始化函数,则调用它。
sched_clock_init(): 初始化调度时钟,用于跟踪和管理任务的执行时间。
calibrate_delay(): 校准延迟函数,用于测量和调整系统的延迟和时钟频率。
pidmap_init(): 初始化进程 ID 映射表,用于管理进程的唯一标识符。
anon_vma_init(): 初始化匿名虚拟内存区域,用于存储无文件映射的页面。
if (efi_enabled(EFI_RUNTIME_SERVICES)) efi_enter_virtual_mode(): 如果启用了 EFI 运行时服务,则进入虚拟模式。
thread_info_cache_init(): 初始化线程信息缓存,用于存储和管理线程的上下文信息。
cred_init(): 初始化进程的凭证,包括用户和组的标识。
fork_init(totalram_pages): 初始化进程的创建和管理机制,设置可用的物理内存大小。
proc_caches_init(): 初始化 proc 文件系统的缓存,用于存储和管理进程的相关信息。
buffer_init(): 初始化缓冲区子系统,用于缓存和管理文件系统的数据块。
key_init(): 初始化密钥管理子系统,用于存储和管理密钥和安全令牌。
security_init(): 初始化安全子系统,用于提供系统的安全功能和策略。
dbg_late_init(): 进行调试和跟踪功能的后期初始化。
vfs_caches_init(totalram_pages): 初始化虚拟文件系统的缓存,用于高效地存储和检索文件系统的元数据。
signals_init(): 初始化信号处理机制,用于进程间通信和异常处理。
page_writeback_init(): 初始化页面回写机制,用于将修改的页面写回到存储设备。
if (CONFIG_PROC_FS) proc_root_init(): 如果启用了 proc 文件系统,则初始化 proc 根目录。
cgroup_init(): 初始化控制组 (cgroup) 子系统,用于管理和限制进程组的资源使用。
cpuset_init(): 初始化 CPU 集合子系统,用于管理和限制进程的 CPU 使用。
taskstats_init_early(): 初始化任务统计子系统,用于收集和跟踪任务的执行统计信息。
delayacct_init(): 初始化延迟账户子系统,用于跟踪和统计延迟操作的使用情况。
check_bugs(): 检查和报告系统中已知的硬件和软件缺陷。
acpi_early_init(): 在 LAPIC (本地高级可编程中断控制器) 和 SMP (对称多处理) 初始化之前,进行 ACPI (高级配置与电源接口) 的早期初始化。
sfi_init_late(): 在初始化 ACPI 之后,进行 SFI (简单固件接口) 的后期初始化。
if (efi_enabled(EFI_RUNTIME_SERVICES)) { ... }: 如果启用了 EFI 运行时服务,则进行 EFI 的后期初始化,并释放引导时的 EFI 服务。
ftrace_init(): 初始化函数跟踪机制,用于跟踪和调试函数的调用流程。
rest_init(): 启动系统的剩余部分,包括创建第一个用户进程和进入主循环。
这些操作继续设置和初始化内核的其他子系统和功能,并开始正式运行内核。请注意,这仅是内核启动过程的一小部分,还有其他步骤和操作被省略了。完整的内核启动过程非常复杂和详细。

浙公网安备 33010602011771号