1. 设备树基础:什么是设备树?为什么需要设备树?设备树与ACPI的区别
各位同学,咱们今天聊点实在的。设备树(Device Tree)这东西,说白了就是一套描述硬件信息的“数据手册”。你想想看,一个嵌入式板子上面有CPU、内存、串口、I2C控制器、GPIO……这些东西怎么让内核知道?总不能让人家内核工程师每换一块板子就改一遍代码吧?
设备树就是来解决这个问题的。它用一套树形结构,把硬件信息写成文本,内核启动时直接解析它,就知道“哦,我这块板子上有个UART3,地址在0x1000A000,中断号是42”。
核心一句话:设备树是描述硬件的结构化数据,让同一份内核镜像能适配不同硬件平台。
1.1 为什么需要设备树?
我刚开始做嵌入式那会儿,用的还是老办法——直接在C代码里写死硬件信息。比如你想加个网卡,就得改 board-xxx.c 文件,然后重新编译内核。换一块板子?对不起,得重新来一遍。
这有什么问题呢?
- 代码膨胀:每个板子都要维护一份 board 文件,Linux 内核里曾经有上千个这样的文件
- 维护噩梦:厂商升级内核时,这些板级代码得跟着改,工作量巨大
- 碎片化:同样的外设,不同板子的初始化代码可能写得五花八门
设备树出现后,情况就变了。硬件描述从C代码里剥离出来,变成纯文本的 .dts 文件。内核启动时,通过解析这个文件来动态初始化硬件。换板子?换一个 .dts 文件就行,内核代码不用动。
我的经验:我在一个项目里遇到过,客户要求把同一套系统移植到三块不同的板子上。如果没有设备树,我得维护三份板级代码,每份几千行。用了设备树,只需要改几十行的 .dts 文件,省了至少两周的工作量。
1.2 设备树长什么样?
咱们直接看个例子。这是一个简单的设备树片段:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0x0>;
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
};
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
clock-frequency = <48000000>;
};
};
你看,结构很清晰:
- 根节点
/描述整个板子 cpus节点描述CPU信息memory节点描述内存布局uart0节点描述串口外设
每个节点里都有 compatible 属性,这是用来匹配驱动的。内核驱动里会声明自己支持哪些 compatible 字符串,设备树里写对了,驱动就能自动绑定上。
注意:compatible 字符串的命名有讲究。一般格式是 "厂商,型号",比如 "ti,am3352-uart"。我曾经见过有人随便写了个 "my_uart",结果内核驱动死活匹配不上,查了半天才发现是命名不规范。
1.3 设备树与ACPI的区别
说到这,可能有同学会问:那ACPI(高级配置与电源管理接口)不也是描述硬件的吗?它俩有啥区别?
嗯,这个问题问得好。我简单说说:
| 对比项 | 设备树 (DT) | ACPI |
|---|---|---|
| 主要应用场景 | ARM、RISC-V 等嵌入式平台 | x86、x86_64 服务器/PC |
| 数据格式 | 纯文本 .dts,编译为二进制 .dtb | AML(ACPI Machine Language)字节码 |
| 灵活性 | 高,可随意修改 .dts 文件 | 低,通常由BIOS/UEFI固件提供 |
| 运行时修改 | 支持设备树覆盖(overlay) | 支持 DSDT 替换,但较复杂 |
| 电源管理 | 功能有限,需额外驱动支持 | 原生支持,功能强大 |
| 热插拔 | 支持有限 | 原生支持(PCIe、USB等) |
说白了,设备树是“嵌入式专用”的轻量方案,ACPI是“PC/服务器”的全功能方案。为什么会有这种差异?
你想想看,嵌入式设备硬件是固定的,板子出厂后基本不会变。设备树简单直接,一个文本文件就能搞定。而PC上呢?你可能今天插个显卡,明天换个网卡,后天加个NVMe硬盘。ACPI需要处理这些动态变化,所以设计得更复杂。
关键区别:设备树是“静态描述”,ACPI是“动态管理”。设备树告诉内核“我有什么”,ACPI告诉内核“我有什么,以及怎么管理它们”。
我记得有一次,一个做x86移植的同事问我:“能不能在x86上用设备树?”理论上可以,但实际没人这么干。因为x86的BIOS/UEFI已经提供了ACPI,你再搞一套设备树,等于重复造轮子,而且ACPI的电源管理、热插拔功能设备树根本替代不了。
反过来,ARM平台上用ACPI的也有,但很少。因为ARM的固件生态不像x86那么统一,设备树更灵活,更适合嵌入式场景。
1.4 小结
这一节咱们讲了三个核心问题:
- 设备树是什么:描述硬件信息的树形数据结构
- 为什么需要它:把硬件描述从内核代码中分离出来,提高可移植性
- 和ACPI的区别:设备树轻量静态,ACPI复杂动态,各有所长
下一节,咱们会深入设备树的语法细节,包括节点、属性、标签、引用这些概念。到时候我会拿一个实际项目里的设备树文件来拆解,保证你看完就能自己写 .dts 文件。
课后小建议:找一块你手头的开发板,看看它的 .dts 文件。别怕看不懂,先熟悉一下结构。我当年就是这么入门的——对着树莓派的设备树文件,一行一行地查文档,慢慢就摸清楚了。