嵌入式Linux内核初始化里程碑路线图及设备树解析时机问询
嵌入式Linux内核初始化里程碑路线图(修正版)
嘿,我来帮你把这个路线图梳理得更清晰,顺便解答你关于设备树的核心疑问,还会修正你现有步骤里的一些细节~
1. Bootloader 启动阶段
- Bootloader(比如U-Boot、GRUB)先完成底层硬件初始化:初始化DDR内存、串口、存储设备等,确保硬件处于可工作状态
- 将内核镜像(vmlinuz)、设备树Blob(dtb文件)加载到RAM的指定物理地址
- 完成启动参数传递(包括dtb在RAM中的地址),设置CPU寄存器后跳转到内核的入口地址
2. 内核汇编入口(head.o)阶段
内核启动的第一步是架构相关的汇编代码(编译自head.S,对应head.o),这部分是纯底层操作:
- 设置临时栈、关闭CPU中断
- 识别当前CPU的架构/型号,初始化核心寄存器
- 启动MMU(内存管理单元):建立临时页表,开启虚拟内存映射,让内核可以使用虚拟地址
- 完成汇编层初始化后,跳转到C语言的核心入口函数
start_kernel()
3. 内核核心初始化(start_kernel())
这是内核初始化的主入口,所有核心子系统的初始化从这里开始:
setup_arch()(关键!设备树解析就在这里):- 从Bootloader传递的参数中读取dtb的物理地址,解析dtb文件,将硬件描述转换为内核内部的
struct device_node节点结构 - 完成CPU架构专属的初始化:设置物理内存布局、初始化中断控制器、系统定时器等核心硬件
- 注册设备树中的平台设备,为后续驱动匹配做准备
- 从Bootloader传递的参数中读取dtb的物理地址,解析dtb文件,将硬件描述转换为内核内部的
- 依次初始化内核核心子系统:内存管理(
page_alloc_init())、进程调度器(sched_init())、中断系统(init_IRQ())、虚拟文件系统(vfs_caches_init())等 - 完成全局核心配置,比如命令行参数解析、CPU数量识别等
4. 驱动与模块初始化(do_initcalls())
- 内核会按优先级顺序调用所有标记为
*_initcall()的初始化函数,优先级从高到低大致为:early_initcall>core_initcall>postcore_initcall>fs_initcall>device_initcall>late_initcall module_init()本质是一个宏,最终会被展开为对应优先级的*_initcall(),所以也会在这个阶段被调用- 注意:设备树只是在
setup_arch()中完成了解析和节点注册,具体的设备驱动匹配、硬件初始化是在这个阶段——驱动通过of_match_table匹配设备树节点,然后执行probe()函数完成硬件初始化
5. 用户空间启动
- 内核完成所有核心初始化后,调用
kernel_init(),最终启动用户空间的第一个进程:默认是/sbin/init(也可以通过内核参数init=/xxx指定其他程序,比如systemd、busybox init) - 至此系统进入用户态,开始执行用户空间的初始化脚本、启动系统服务
关于设备树阶段的明确解答
你问的设备树解析及节点加载,是在**do_initcalls()之前**,具体就是在setup_arch()函数执行过程中完成的。
- 简单说:
setup_arch()负责“读懂”硬件描述(dtb),把硬件信息告诉内核;而do_initcalls()阶段是驱动根据这些信息去初始化实际的硬件。
对你原有路线图的修正建议
- 补充Bootloader传递dtb的步骤:嵌入式Linux启动时,Bootloader必须加载dtb并告知内核其地址,这是硬件描述的关键来源,不能省略
- 明确MMU启动时机:MMU是在head.o的汇编阶段启动的,不是在
setup_arch()之后,这是开启虚拟内存的核心步骤 - 拆分
start_kernel()的核心逻辑:你原来的“加载内核子系统”太笼统,setup_arch()是这个阶段的核心,包含了设备树解析和架构初始化,需要单独强调 - 区分设备树解析与驱动初始化:设备树只是硬件信息的“说明书”,真正的硬件初始化是驱动在
do_initcalls()阶段完成的,不要混淆这两个环节
内容的提问来源于stack exchange,提问作者mozcelikors




