内核自解压阶段做了什么

当引导加载程序将压缩的内核镜像加载到内存后,处理器首先跳转到自解压代码入口。这个阶段由arch/arm/boot/compressed/head.S中的汇编指令完成,主要工作是解压缩内核并处理设备树信息。解压代码会检测自身是否与解压后的目标地址重叠arm linux内核启动流程,如果重叠则先将自己重定位到安全区域,再调用解压函数将zImage还原为原始的内核镜像。

对于ARM Linux,自解压过程中还会完成必要的MMU初始化,但通常只是临时的。解压完成后arm linux内核启动流程,代码会跳转到解压后的内核入口点,即arch/arm/kernel/head.S中的stext标号。需要注意,如果内核配置了XIP(就地执行),则不需要自解压阶段,但大多数主流ARM平台仍使用压缩内核以节省存储空间。

linux内核启动_linux内核启动流程概述_arm linux内核启动流程

内核入口stext在哪里

stext是ARM Linux内核启动的第一个C语言环境之前的汇编入口,位于arch/arm/kernel/head.S文件中。这个标号负责完成最低级的处理器初始化工作,包括设置异常向量表、检测当前CPU类型、验证机器ID(老式ATAG方式)或读取设备树中的兼容字符串。stext的第一步是关闭所有中断和MMU,确保内核在实地址模式下运行。

内核会调用__lookup_processor_type函数,根据处理器ID寄存器值匹配内核预编译支持的CPU列表。如果匹配失败则挂起;成功则继续调用__vet_atags检查启动参数(ATAG或设备树地址)。随后启用一级页表的临时映射,通常是映射内核自身所在的地址段和必要的设备内存区域。最终stext会跳转到__mmap_switched,正式进入C运行环境。

内核如何初始化处理器

linux内核启动_linux内核启动流程概述_arm linux内核启动流程

进入__mmap_switched后,内核开始为C代码执行做准备:清理BSS段、设置栈指针、保存启动参数地址等。然后调用start_kernel函数,这是所有Linux内核架构共同的入口点。start_kernel首先调用setup_arch,这是一个体系结构相关的初始化函数,在ARM中主要完成处理器核心、缓存、协处理器以及设备树的解析。

setup_arch具体会调用cpu_init来初始化每个CPU的寄存器状态和异常栈linux 内核,调用early_trap_init设置异常向量表,并通过clk和pm等子系统初始化底层时钟和电源管理。同时,setup_arch会根据设备树中的“chosen”节点获取命令行参数,初始化内存银行信息,并建立物理内存描述符。完成这些后,处理器已经具备运行复杂C语言代码和调度任务的基础能力。

内存管理初始化流程

arm linux内核启动流程_linux内核启动_linux内核启动流程概述

ARM Linux内核在启动过程中会经历三个内存管理阶段:初期临时映射、核心页表建立和完整虚拟内存系统启用。在setup_arch中,setup_machine_fdt负责识别设备树并调用arm_memblock_init构建内存块分配器。memblock会收集所有物理内存区域和预留区域为后续页表分配做准备然后在paging_init中建立最终的页表映射。

paging_init调用map_lowmem将低端物理内存线性映射到内核虚拟地址空间,同时调用devicemaps_init为设备内存建立映射。之后内核会初始化每个CPU的TLB和缓存,通过flush_cache_all保证数据一致性。当内存管理完全初始化完成后,内核会调用mm_init通知伙伴系统接管内存分配。至此malloc和页面分配器都可以正常工作,后续所有驱动和设备初始化都依赖于此。

调度器和中断初始化

在内存管理就绪后,start_kernel继续调用sched_init来初始化调度器数据结构。该函数会创建每个运行队列(runqueue),初始化空闲进程linux软件工程师,并启用负载均衡的默认参数。同时调用init_IRQ来设置中断控制器,对于ARM平台主要是GIC驱动初始化,包括探测中断类型、设置中断描述符表以及注册默认中断处理程序。

紧接着内核调用early_irq_init建立中断域(irq_domain)以支持中断号映射,再调用init_IRQ完成实际硬件中断控制器的配置。随后调用tick_init和init_timers设置时钟源和时钟事件设备,为调度器提供时间基准。完成这些后,内核已经可以响应外设中断并按照优先级调度进程。最后调用softirq_init初始化软中断机制,为网络和块设备子系统做好准备。

启动用户空间第一个进程

linux内核启动_arm linux内核启动流程_linux内核启动流程概述

内核完成所有核心子系统初始化后,会创建并启动第一个用户空间进程。通过调用kernel_thread创建init进程(进程ID为1),该进程最终执行根文件系统中的init程序。在ARM平台上,init进程会尝试加载/etc/inittab或直接执行/sbin/init。同时内核会创建kthreadd内核线程(进程ID为2)来管理其他内核线程,然后调用rest_init进入空闲循环。

如果根文件系统找不到init程序,内核就会触发panic并停留在控制台输出错误信息。为了确保init进程顺利启动,开发者必须在设备树或启动参数中正确指定root设备路径和文件系统类型。内核最后会调用cpu_startup_entry进入idle循环,当CPU空闲时执行WFI指令降低功耗。至此,整个ARM Linux内核启动流程全部完成,系统进入用户空间操作的正常工作模式。

看完这篇文章,你在实际移植ARM Linux内核时是否遇到过启动卡在某个阶段的问题?欢迎在评论区分享你的调试经验,点赞和转发让更多嵌入式开发者一起进步!

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复