1. 课程导入
1.1 什么是设备树?
设备树,英文叫 Device Tree,是一套用来描述硬件资源的数据结构。说白了,它就是一份硬件的「简历」——告诉内核:你有哪些外设、它们挂在哪条总线上、中断号是多少、寄存器地址范围是什么。
我刚开始接触嵌入式 Linux 时,总觉得设备树很神秘。后来自己手动适配过几块板子,才明白它其实就是一堆 .dts 文件,编译成 .dtb 后交给内核解析。内核不再需要硬编码硬件信息,而是运行时从设备树里读。
举个例子,一个简单的 UART 节点长这样:
/dts-v1/;
/ {
model = "MyBoard";
compatible = "vendor,myboard";
soc {
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
clock-frequency = <1843200>;
status = "okay";
};
};
};
你看,compatible 告诉内核用哪个驱动,reg 给出地址和大小,interrupts 描述中断信息。内核启动时,会遍历设备树,为每个匹配的节点加载驱动。
核心要点:设备树是硬件描述与内核驱动之间的「契约」。驱动不直接知道硬件在哪,而是通过设备树找到它。
1.2 为什么需要设备树?
在设备树出现之前,Linux 内核是怎么处理硬件的?答案是——硬编码。每个板子都在 arch/arm/mach-xxx 下写一堆 board-xxx.c 文件,里面塞满平台设备、资源、中断号。你想想看,如果内核要支持 100 块板子,就得有 100 个这样的文件。维护起来简直是噩梦。
我印象很深,有一次我接手一个老项目,内核版本还是 2.6.35。为了加一个新板子,我得复制一份 board-xxx.c,改几十个宏定义,再重新编译整个内核。改错一个地址,系统就起不来。调试全靠 printk,效率极低。
设备树解决了三个核心问题:
- 硬件与驱动解耦——同一份内核镜像可以跑在不同板子上,只要换
.dtb文件就行。 - 减少内核代码量——不再需要成百上千的
board-xxx.c,所有硬件描述集中到.dts里。 - 启动时动态适配——内核在启动阶段解析设备树,动态创建设备,而不是编译时静态绑定。
我的经验:如果你在维护一个多板卡的产品线,设备树能让你少掉很多头发。我见过一个团队,从 15 个板卡文件缩减到 3 个 .dtsi 公共包含文件,维护成本直线下降。
1.3 设备树与 ACPI 的区别
很多人会问:ACPI 不也是描述硬件的吗?为什么嵌入式用设备树,PC 和服务器用 ACPI?
嗯,这里要注意。ACPI 和设备树虽然目标相似,但设计哲学完全不同。我整理了一个对比表,方便你理解:
| 对比维度 | 设备树 (DT) | ACPI |
|---|---|---|
| 起源 | Open Firmware,PowerPC 时代 | PC 行业,Intel/Microsoft 主导 |
| 描述方式 | 纯文本 .dts,编译为二进制 .dtb |
AML 字节码,由 BIOS/UEFI 提供 |
| 运行时行为 | 静态描述,内核解析后不再变化 | 支持动态方法调用(如 _STA、_ON、_OFF) |
| 电源管理 | 依赖驱动自身实现 | 内置电源管理对象(P-State、C-State) |
| 适用场景 | 嵌入式、ARM、RISC-V | x86 PC、服务器、笔记本 |
| 复杂度 | 相对简单,学习曲线平缓 | 非常复杂,ASL 语言门槛高 |
| 修改方式 | 改 .dts 重新编译即可 |
需要修改 BIOS/UEFI,风险大 |
为什么嵌入式不用 ACPI?我个人觉得有两点:
- ACPI 太重了——它需要完整的 BIOS/UEFI 栈,嵌入式设备哪有这个?
- ACPI 的 AML 解释器——内核里有个庞大的 AML 解释器,占内存不说,还容易出 bug。嵌入式设备讲究精简,设备树这种静态描述更合适。
我曾经踩过的坑:有次我在一个 ARM 服务器上尝试用 ACPI 替代设备树,结果发现 ACPI 的命名空间和中断路由模型跟 ARM GIC 完全不兼容。折腾了两周,最后还是老老实实改回设备树。所以,别轻易跨界——ARM 生态就用设备树,x86 生态就用 ACPI。
1.4 设备树的核心知识结构
为了让你对整个设备树体系有个全局认识,我画了一张结构图。它展示了设备树从源文件到内核驱动的完整链路:
从这张图你可以看到,设备树的整个生命周期很清晰:写 .dts → 编译成 .dtb → Bootloader 加载 → 内核解析 → 驱动匹配 → 设备注册。每一步都有对应的工具和 API 支撑。
我的建议:刚开始学设备树,别急着啃完整规范。先学会看懂一个简单的 .dts 文件,然后用 dtc -I dtb -O dts 反编译现有的 .dtb,看看实际板子是怎么描述的。动手拆解,比看十遍文档都管用。