1. 设备树概述:起源、作用与核心地位

1.1 设备树的起源:从“硬编码”到“描述”的范式转变

在设备树(Device Tree, DT)出现之前,嵌入式 Linux 内核中充斥着大量针对特定单板(Board)的 “硬编码” 代码。每个板级文件(如 arch/arm/mach-xxx.c)都需要手动注册平台设备、定义内存地址、中断号、GPIO 映射等。这种模式导致内核代码冗余、维护困难,且每更换一款硬件,就必须重新编译内核。

设备树的概念源于 Open Firmware(IEEE 1275),由 PowerPC 架构率先采用。ARM 架构在 Linux 3.x 时代全面引入设备树,彻底改变了硬件描述方式。其核心思想是:将硬件配置信息从内核代码中剥离,以结构化数据的形式传递给内核,实现“一次编译,多处运行”。

关键转折点: 设备树的引入使得同一份内核镜像可以支持不同硬件配置的板卡,Bootloader 只需加载对应的 DTB(Device Tree Blob)文件即可。
设备树起源:从硬编码到描述式硬件抽象 旧模式(无设备树) • 板级文件硬编码 • 平台设备手动注册 • 内存/中断地址写死 • 每板必改内核源码 • 维护成本极高 设备树模式 • DTS 源文件描述硬件 • DTB 二进制传递 • 内核解析设备节点 • 驱动匹配 compatible • 硬件配置与内核解耦 结果 ✅ 一次编译 ✅ 多板支持 ✅ 易于维护 设备树本质:一种描述硬件资源的树形数据结构,而非可执行代码。

1.2 设备树的作用:硬件描述与内核解耦

设备树在嵌入式 Linux 系统中承担着 “硬件配置清单” 的角色。它使用节点(Node)和属性(Property)来描述系统中的所有可发现硬件资源,包括但不限于:

  • CPU 核心:数量、架构、缓存大小。
  • 内存布局:起始地址、大小、DMA 区域。
  • 总线拓扑:AMBA、PCIe、I2C、SPI、USB 等。
  • 外设寄存器:基地址、中断号、时钟频率。
  • 引脚复用:GPIO 功能配置、电气特性。
  • 电源管理:电压域、时钟门控、休眠状态。

内核在启动阶段解析 DTB,根据节点信息动态创建 platform_device、i2c_client、spi_device 等内核设备对象,并触发对应驱动程序的 probe() 回调。这一机制使得驱动代码不再依赖具体的板级配置,而是通过 compatible 属性与设备节点进行匹配。

功能维度 设备树实现方式 传统硬编码方式
硬件描述 DTS 源文件 + DTB 二进制 C 结构体 + 宏定义
设备注册 内核解析 DTB 自动注册 手动调用 platform_device_register()
资源传递 节点属性(reg, interrupts, clocks) 硬编码 resource 结构体
驱动匹配 compatible 字符串匹配 设备 ID 表 + 板级检查
多板支持 更换 DTB 文件即可 重新编译内核或添加板级文件
实践提示: 在调试设备树时,可以使用 dtc -I dtb -O dts 命令将 DTB 反编译为可读的 DTS 格式,快速验证 Bootloader 传递的 DTB 内容是否正确。

1.3 设备树与 ACPI 的区别:嵌入式 vs 通用计算

ACPI(Advanced Configuration and Power Interface)是 x86 架构的标准硬件抽象层,主要用于 PC 和服务器领域。设备树与 ACPI 虽然目标相似(硬件描述与操作系统解耦),但在设计哲学、使用场景和实现细节上存在显著差异。

设备树 vs ACPI:核心差异对比 设备树 (Device Tree) • 嵌入式/ARM 主导 • 静态编译,启动时加载 • 纯硬件拓扑描述 • 无运行时热插拔标准 • 内核解析,驱动匹配 • 开源社区维护 • 文件格式:DTS/DTB ACPI • x86/PC/服务器主导 • 固件(BIOS/UEFI)生成 • 硬件 + 电源管理 + 热插拔 • 支持运行时设备枚举 • 操作系统解释 AML 字节码 • 工业标准组织维护 • 表格式:DSDT/SSDT 选择依据:ARM 生态默认设备树;x86 生态默认 ACPI;RISC-V 生态两者皆可。
对比维度 设备树 (DT) ACPI
主要应用架构 ARM、RISC-V、PowerPC x86、x86_64、IA64
描述语言 DTS(文本)+ DTB(二进制) ASL(文本)+ AML(字节码)
运行时能力 静态,启动后不变 动态,支持热插拔、电源事件
电源管理 依赖驱动 + 独立 PM 框架 内建 P-state/C-state 控制
固件交互 Bootloader 传递 DTB 地址 UEFI 提供 ACPI 表根指针
复杂度 较低,易于理解和调试 较高,AML 解释器复杂
社区/标准 Linux 内核社区维护 UEFI 论坛标准化
注意: 虽然设备树和 ACPI 在功能上有重叠,但在嵌入式 Linux 领域,设备树是绝对的主流。ACPI 在 ARM 服务器领域(如 Ampere、华为鲲鹏)也有应用,但嵌入式场景几乎全部使用设备树。

1.4 设备树在嵌入式 Linux 中的核心地位

在嵌入式 Linux 系统中,设备树已经从一个可选的辅助工具演变为 不可或缺的核心组件。它贯穿了从 Bootloader 到内核、再到驱动加载的整个启动流程:

  1. Bootloader 阶段:U-Boot 或 Barebox 从存储介质(eMMC、NAND Flash、SD 卡)读取 DTB 文件,或根据硬件跳线动态生成 DTB,将其加载到内存中,并将 DTB 物理地址传递给内核。
  2. 内核启动早期:内核解压后,首先调用 early_init_dt_scan() 解析 DTB,获取内存大小、CPU 数量、chosen 节点(如 bootargs)等关键信息。
  3. 设备注册阶段:内核遍历 DTB 中的节点树,为每个匹配驱动的节点创建设备对象,并调用驱动的 probe() 函数。
  4. 运行时阶段:驱动通过 of_* 系列 API(如 of_property_read_u32())动态读取设备树属性,获取硬件配置参数。
设备树在嵌入式 Linux 启动流程中的核心位置 Bootloader 加载 DTB 到内存 传递 DTB 地址 内核早期 解析 DTB 头部 获取内存/CPU 信息 设备注册 遍历节点树 创建 platform_device 驱动运行时 of_property_read_* 读取硬件配置 设备树核心地位总结 硬件描述的唯一权威来源 | 驱动与硬件解耦的桥梁 | 多板兼容的基础 Bootloader 必须正确传递 DTB,否则内核无法正常启动或驱动加载失败
核心结论: 设备树是嵌入式 Linux 系统中连接 Bootloader 与内核、硬件与驱动的“中间语言”。掌握设备树的加载阶段与 DTB 参数配置,是嵌入式系统工程师的必备技能。后续章节将深入分析 Bootloader 如何传递 DTB、内核如何解析 DTB,以及常见的配置错误与调试方法。