1. 设备树基础:从一块砖头说起

我记得刚入行那会儿,做嵌入式Linux移植,最头疼的就是板子换了颗Flash芯片,内核代码得改好几个地方。那时候我就在想——有没有一种办法,把硬件描述和内核源码彻底分开?

后来我遇到了设备树。说白了,它就是一套描述硬件信息的“配置文件”。你换硬件,改这个文件就行,不用动内核代码。

1.1 设备树的起源与作用

设备树的全称是 Device Tree,最早源于PowerPC架构。Linux内核在2.6时代开始引入,到了3.x版本,ARM架构全面拥抱它。

为什么需要它?你想想看,以前每个板子都要在arch/arm/mach-xxx目录下写一堆board-xxx.c文件,里面全是GPIO、中断、时钟的硬编码。内核每支持一个新板子,就得加一堆C代码。这哪受得了?

设备树的作用,就是把这些硬件信息抽出来,放到一个独立的文本文件里。内核启动时解析这个文件,动态构建硬件拓扑。这样一来:

  • 内核与硬件解耦:换板子不用重新编译内核
  • 同一内核镜像:可以启动不同硬件配置的板子
  • 维护成本降低:硬件描述集中管理,不用到处翻C代码

核心思想:设备树 = 硬件拓扑的“数据驱动”描述。内核只负责解析,不负责硬编码。

1.2 设备树语法基础

设备树的源文件后缀是 .dts,编译后的二进制是 .dtb。语法看起来有点像JSON和C的混合体。我刚开始看的时候也觉得别扭,但用多了就习惯了。

先看一个最简单的例子:

/dts-v1/;

/ {
    model = "MyBoard V1.0";
    compatible = "vendor,myboard-v1", "vendor,myboard";

    #address-cells = <1>;
    #size-cells = <1>;

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;
    };

    chosen {
        bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2";
    };

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        serial@10000000 {
            compatible = "vendor,uart";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            clock-frequency = <24000000>;
        };
    };
};

这里有几个关键点,我一个个说:

  • 根节点:/ 是设备树的根,所有设备都在它下面
  • 属性:compatible、reg、interrupts 这些都是属性,描述设备的特征
  • 地址编码:#address-cells 和 #size-cells 决定地址和长度用几个32位整数表示
  • reg属性:格式是 <地址 长度>,比如 reg = <0x80000000 0x10000000> 表示从0x80000000开始,256MB空间

我的习惯:写设备树时,先把根节点的 model 和 compatible 写好。这两个属性是内核匹配板子的关键,写错了板子起不来。

1.3 设备树与ACPI的对比

ACPI(高级配置与电源管理接口)是x86平台的硬件描述标准。设备树和ACPI虽然干的是同一件事——描述硬件,但思路完全不同。

我做过一个项目,需要在同一套硬件上同时支持Linux和Windows。Linux用设备树,Windows用ACPI。那段时间我天天跟这两套东西打交道,感触很深。

对比维度 设备树 (DT) ACPI
起源 PowerPC/嵌入式 x86/PC
文件格式 文本(.dts) → 二进制(.dtb) AML字节码(二进制)
可读性 高,文本直接可读 低,需要反编译
运行时修改 支持overlay动态加载 支持DSDT替换
电源管理 弱,需要额外驱动 强,原生支持
热插拔 支持有限 原生支持
适用场景 嵌入式、ARM、RISC-V x86、服务器、笔记本

说白了,设备树更“轻”,ACPI更“重”。设备树适合资源受限的嵌入式系统,ACPI适合功能复杂的PC平台。

我曾经踩过的坑:在ARM服务器上尝试用ACPI替代设备树。结果发现ACPI的AML解释器占用的内存比设备树整个都大。后来还是老老实实用回了设备树。选型时一定要考虑硬件资源。

1.4 知识体系总览

下面这张图,是我自己总结的设备树知识体系。你看一眼,心里就有谱了:

设备树知识体系总览 设备树知识体系总览 设备树 (DT) 语法基础 作用与价值 与ACPI对比 节点/属性 reg/中断 硬件解耦 动态加载 维护简化 轻量级 嵌入式专用 文本可读 核心要点 设备树 = 硬件描述语言 + 动态加载机制 + 跨平台兼容 掌握语法、理解作用、对比ACPI,是入门设备树的三大支柱

嗯,这张图把设备树的三个核心维度串起来了。语法是基础,作用是价值,对比是定位。你把这三点吃透了,后面学设备树集成和固件裁剪就会轻松很多。

1.5 小结

设备树不是什么高深的东西。它就是一套描述硬件的规则,让内核和硬件解耦。我见过很多新手一上来就啃设备树binding文档,结果越看越晕。我的建议是:先写一个最简单的.dts,编译成.dtb,让内核跑起来。然后再慢慢加节点、调属性。

实践出真知,这句话在设备树上特别适用。