知晓ARM Linux内核启动的流程,属于深入嵌入式系统去开展开发以及内核调试的根基。此过程起始于处理器进行上电复位,一直到最终把控制权转交给位于用户空间的首个进程,期间涵盖硬件初始化、引导加载程序、内核解压与重定位、核心数据结构构建等一连串精密的操作。弄清每个阶段的细节作用,能够协助开发者去定位启动时出现的故障、优化启动所需时间,亦或是针对特定硬件予以定制。

ARM Linux内核从哪里开始执行

当开发板接通电源,ARM CPU的复位向量地址朝着BootLoader而去(像U-Boot此类) 。BootLoader的责任是对最基础的内存控制器、时钟等等进行初始化,以此为加载内核镜像做好相应准备 。它从存储设备(比如eMMC、SD卡)那儿读取压缩的zImage内核镜像来到内存所指定的位置,并且有可能会把包含内存大小、命令行等相关信息的ATAGs或者Device Tree Blob(DTB)传递至于内核 。

linux内核启动_linux内核开发流程_arm linux内核启动流程

BootLoader跳转至内核入口点,此入口点是解压代码起始之处。此位置并非内核的main函数,乃是一段与架构紧密关联的汇编启动代码。这段代码负责把CPU切换至安全模式,比如SVC模式,关闭中断,构建最小运行环境。它的首要任务是进行自解压,把真正的内核映像从当前位置解压至内存的最终运行地址,预备进入内核的正式启动阶段。

内核解压后如何初始化关键硬件

解压结束之后,执行的权力交付给内核的head.S等相应的汇编文件的手中。这一部分的代码全部都是运用汇编进行编写的arm linux内核启动流程,目标是在C语言运行的环境构建完成之前,达成最底层的硬件设置的任务。它会开展 设置虚拟内存映射的页表的工作,启动MMU,把处理器从物理地址的世界转变到虚拟地址的空间范围当中。这对于后续所有内核代码的正常寻址而言是具有极其重要的价值的。

arm linux内核启动流程_linux内核启动_linux内核开发流程

它会对处理器缓存进行初始化,以此为数据访问加快速度。与此同时,将栈指针初始化linux教程下载,为后续调用C函数做准备。在此之后呢,代码会跳转至第一个C语言函数start_kernel。到了这个时候,体系结构相关的低级别初始化就算消停了,内核进入到与架构无关却关键的通用初始化流程,启动过程也开始变得更易于阅读且具备可扩展性了。

start_kernel函数做了哪些核心工作

start_kernel乃Linux内核之“主函数”,其处于init/main.c内,担当初始化内核所有核心子系统之责。首先,它会施行陷阱处理、设置中断向量表,且早期初始化控制台,以使printk能输出信息,此为后续所有调试信息之根基。与此同时,初始化内存管理的数据结构,标记可用以及已用的物理内存页。

它会对调度器进行初始化,去创建0号进程也就是idle进程以及1号进程也就是init进程的内核线程。它会对定时器进行初始化,还会对中断控制器比如GIC进行初始化,并且会扫描设备树DTB,把硬件信息传递给内核。在这个进程当中,内核逐渐地不再处于“裸”状态而变得“丰满”起来,各类核心机制像是进程管理、内存管理以及中断处理等都已然就绪,为驱动以及模块的加载营造了条件。

内核如何初始化驱动程序并挂载根文件系统

当内核子系统准备就绪之后,便会进入到rest_init函数之中,此函数会创建内核线程用以执行kernel_init。该函数承担着处理经由命令行传入的“init=”参数或者默认路径的职责。然而在这之前,驱动程序的初始化工作是必须要完成的。内核会调用一系列的*_initcall函数,这些函数乃是驱动程序或者模块借助宏声明而注册的初始化函数,会依照优先级的顺序来执行。

起着驱动初始化关键作用的是块设备驱动以及文件系统驱动,块设备驱动像MMC、SATA之类的,仅当它成功完成初始化之后,内核才能够去访问存储设备所在之处随后依次进行的是,初始化与之对应的文件系统,像ext4这种类型的接着尝试去挂载由命令行参数“root=”所明确指定的分区当作根文件系统,根文件系统挂载成功这件事所蕴含的意义在于,内核能够读取磁盘之上的可执行程序以及库文件,而这恰恰是启动用户空间的前提条件 。

arm linux内核启动流程_linux内核启动_linux内核开发流程

用户空间的第一个进程怎样被启动

根文件系统挂载达成成功状态之后,内核会去寻觅并施行根文件系统里的首个用户空间程序。在默认的状况之下,这个程序是/sbin/init,不过也能够借助内核命令行参数“init=”予以指定。内核借由run_init_process函数调用execve系统调用,试着去执行此程序。假如,/sbin/init的执行出现失败状况,那么,内核会依照顺序,去尝试诸如,/etc/init、/bin/init等之类的路径。

若这个init程序,像systemd、BusyBox init这般,被成功执行了,那么内核启动流程便正式完结。CPU的控制权彻底移交至用户空间。init进程会依据其配置文件,比如说inittab或者systemd的unit文件,持续启动系统里的各类服务与应用,最终达成整个操作系统的启动,向用户展现登录界面或者应用程序界面。

如何调试ARM Linux内核的启动过程

linux内核开发流程_linux内核启动_arm linux内核启动流程

调试启动失败时,最常见的办法是运用串口控制台。于内核配置里启用CONFIG_DEBUG_LL以及CONFIG_EARLY_PRINTK,能够让内核自初始化早期便经由串口输出信息arm linux内核启动流程,这对定位在控制台全然初始化之前所发生的错误有益。此外,认真核对BootLoader传递给内核的ATAGs或者DTB参数,尤其是内存地址与大小,乃是避免启动卡死的关键。

对于更为复杂的启动问题而言linux计划任务,或许需要借助比如JTAG之类的硬件调试工具来开展单步跟踪。与此同时,剖析内核的启动日志(借助dmesg去查看)乃为重要手段。日志当中会依照顺序记录各个子系统的初始化状态。经由调整内核命令行里的“loglevel”参数,能够输出更为详细的调试信息,用以帮助逐段排查初始化失败的具体模块以及函数调用。

于实际开发或者学习进程里,当碰到与ARM Linux内核启动有关联的问题之际,最为经常卡在的是哪一个环节?是BootLoader配置这一项么,还是设备树修改这一点,又或者是内核驱动初始化这个情况!祈盼在评论区域分享所你的经验以及困惑,同时也千万不要忘记点赞以及转发,从而让更多的开发者朋友一块儿交流研究探讨 。

Tagged:
Author

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

刘遄

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

发表回复