You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

嵌入式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架构专属的初始化:设置物理内存布局、初始化中断控制器、系统定时器等核心硬件
    • 注册设备树中的平台设备,为后续驱动匹配做准备
  • 依次初始化内核核心子系统:内存管理(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()阶段是驱动根据这些信息去初始化实际的硬件。

对你原有路线图的修正建议
  1. 补充Bootloader传递dtb的步骤:嵌入式Linux启动时,Bootloader必须加载dtb并告知内核其地址,这是硬件描述的关键来源,不能省略
  2. 明确MMU启动时机:MMU是在head.o的汇编阶段启动的,不是在setup_arch()之后,这是开启虚拟内存的核心步骤
  3. 拆分start_kernel()的核心逻辑:你原来的“加载内核子系统”太笼统,setup_arch()是这个阶段的核心,包含了设备树解析和架构初始化,需要单独强调
  4. 区分设备树解析与驱动初始化:设备树只是硬件信息的“说明书”,真正的硬件初始化是驱动在do_initcalls()阶段完成的,不要混淆这两个环节

内容的提问来源于stack exchange,提问作者mozcelikors

火山引擎 最新活动