嵌入式系统Linux内核初始化里程碑规划及设备树处理时机问询
嵌入式Linux内核初始化里程碑路线图(修正&补充版)
嘿,我来帮你把这个流程理清楚,先修正你原来梳理里的小细节,再重点拆解设备树的处理时机:
完整初始化里程碑
Bootloader 前置阶段
- Bootloader(比如U-Boot)先完成基础硬件初始化(串口、RAM、时钟等),把内核镜像、**设备树Blob(DTB)**一起加载到RAM的指定地址,然后跳转到内核的入口地址启动内核。
内核汇编启动(head.o)
- 内核首先进入和架构强绑定的汇编代码(比如ARM平台的
head.S),完成最底层的初始化:- 设置内核栈、禁用全局中断
- 识别当前CPU的架构/型号,配置核心寄存器
- 启动MMU(这一步是在汇编阶段完成的,开启后才会进入C语言环境的
start_kernel(),你原来的顺序没问题,但要明确阶段) - 最后跳转到C语言的核心入口
start_kernel()
- 内核首先进入和架构强绑定的汇编代码(比如ARM平台的
内核核心初始化(start_kernel())
- 这是整个内核初始化的主入口,会依次初始化全局核心组件:
- 初始化调度器、内存管理系统、
printk打印框架等基础核心 - 调用
setup_arch()——这就是设备树处理的关键节点!- 在
setup_arch()中,内核会从Bootloader传递的地址读取DTB文件,完成设备树的解析工作:把二进制的DTB转换成内核内部可操作的device_node结构体树 - 同时
setup_arch()还会依赖设备树提供的硬件信息,完成CPU架构相关的其他初始化(比如系统内存布局、总线架构的初始化)
- 在
- 加载内核核心子系统框架(比如PCI总线、字符/块设备框架等)
- 初始化调度器、内存管理系统、
- 这是整个内核初始化的主入口,会依次初始化全局核心组件:
驱动与模块初始化(do_initcalls())
- 内核调用
do_initcalls(),按优先级依次执行所有带*_initcall()标记的初始化函数(module_init()本质是封装成device_initcall级别的调用) - 这里是设备树节点被具体使用的阶段:各个驱动在初始化时,会通过
of_match_table匹配设备树中的对应节点,读取节点里的硬件参数(比如寄存器基地址、中断号、时钟频率),完成设备的实例化和初始化
- 内核调用
进入用户空间
- 内核完成所有初始化后,通过
kernel_init()启动用户空间的第一个进程:通常是/sbin/init,也可能是systemd、busybox init等替代程序
- 内核完成所有初始化后,通过
设备树处理时机的核心总结
针对你的疑问,这里明确两个关键阶段:
- 设备树的解析(从DTB到内核内部结构):在
setup_arch()中完成,这一步远早于do_initcalls() - 设备树节点的实际使用(驱动匹配、参数读取):在
do_initcalls()阶段,由各个驱动的初始化函数分别处理
对你原梳理内容的小修正
- 启动MMU的时机:是在
head.o的汇编代码阶段,而非进入start_kernel()之后(start_kernel()是MMU开启后才进入的C语言环境) - 设备树的加载和解析是
setup_arch()的核心工作之一,并不是在do_initcalls()阶段才开始的 do_initcalls()是按优先级执行初始化函数的,module_init()对应的是device_initcall优先级,会在核心子系统初始化完成后执行
内容的提问来源于stack exchange,提问作者mozcelikors




