1、设备树基础回顾:什么是设备树?为什么需要设备树?设备树在Linux内核中的角色。

各位同学好,我是老李。做嵌入式Linux开发十几年了,今天咱们聊聊设备树。说实话,我刚入行那会儿,还没有设备树这东西。那时候板级文件满天飞,每次换个Flash型号,都得改一堆C代码。嗯,那段日子,想想都头疼。

设备树(Device Tree),说白了就是一套描述硬件资源的数据结构。它用文本文件把CPU、内存、外设、中断、GPIO这些东西全说清楚。内核启动时,直接解析这个文件,就知道板子上有什么硬件了。

核心定义:设备树是一种用于描述硬件配置的树形数据结构,以.dts(Device Tree Source)文件形式存在,编译后生成.dtb(Device Tree Blob)供内核使用。

1.1 为什么需要设备树?

这个问题,我当年也问过自己。你想想看,在Linux 2.6时代,ARM架构的板级代码有多乱?每个板子都要在arch/arm/mach-xxx目录下写一堆board-xxx.c文件。这些文件里全是硬件初始化代码,比如:

static struct resource smc911x_resources[] = {
    [0] = {
        .start = 0x10000000,
        .end   = 0x100000ff,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_GPIO_A(5),
        .end   = IRQ_GPIO_A(5),
        .flags = IORESOURCE_IRQ,
    },
};

这种代码,每个板子一份。内核社区维护起来,简直要疯。我记得有一次,我为了给一个老平台加个新网卡,翻了三天板级文件,最后发现中断号写错了。这种痛苦,经历过的人都懂。

设备树解决了三个核心问题:

  • 硬件描述与内核代码解耦:硬件信息不再硬编码在C代码里,而是放在独立的.dts文件中。
  • 单内核镜像支持多平台:同一个内核,换不同的.dtb,就能跑不同的板子。
  • 驱动开发标准化:驱动通过设备树节点获取资源,不再依赖板级代码。

我的经验:早期做BSP移植时,最怕的就是板级文件冲突。两个板子共用一个SoC,但外设地址不同,就得写两套代码。设备树出来后,这种问题基本绝迹了。你只需要维护一份SoC的.dtsi,板级差异放在.dts里就行。

1.2 设备树在Linux内核中的角色

设备树在内核启动流程中,扮演着“硬件导游”的角色。内核启动时,Bootloader把.dtb文件加载到内存,然后把地址传给内核。内核解析这个文件,构建出设备树结构体,驱动再从中获取资源。

我画了一张图,帮你理解这个流程:

设备树在内核启动中的角色 设备树在内核启动中的角色 Bootloader 加载 .dtb 到内存 内核启动 解析设备树 驱动 获取资源 设备树内部结构(简化) / (根节点) cpus memory soc uart i2c 说明:Bootloader 加载 .dtb → 内核解析 → 驱动通过 of_ 系列 API 获取硬件资源

这张图展示了设备树从加载到使用的完整链路。内核解析设备树后,会生成一个struct device_node的树形结构。驱动通过of_match_table匹配节点,然后调用of_iomap()、irq_of_parse_and_map()等API获取资源。

1.3 设备树的核心语法

设备树的语法其实不复杂。我习惯把它理解成“硬件配置的JSON”。一个典型的节点长这样:

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  /* 256 MB */
    };

    &uart0 {
        status = "okay";
        pinctrl-0 = &uart0_pins;
    };
};

这里有几个关键点:

  • compatible:驱动匹配的关键字符串,内核通过它找到对应的驱动。
  • reg:描述地址和大小,格式是<address size>。
  • status:控制设备是否启用,"okay"表示启用,"disabled"表示禁用。
  • &uart0:这是引用语法,用于在根节点外修改已有节点。

注意:我曾经在项目中遇到过一个坑——reg属性的地址长度写错了。比如DDR是256MB,地址范围应该是0x80000000 0x10000000,结果我写成了0x80000000 0x20000000。内核启动时直接崩溃,查了两天才发现是设备树的问题。所以,reg里的size一定要和硬件手册对清楚。

1.4 设备树与性能优化的关系

你可能会问:设备树跟性能优化有什么关系?

关系大了。设备树里配置的参数,直接影响硬件的工作模式。比如:

配置项 影响 优化方向
clock-frequency 外设时钟频率 调整到硬件支持的最高稳定频率
dma-coherent DMA一致性 开启后减少缓存同步开销
interrupt-affinity 中断亲和性 绑定到指定CPU核,减少中断延迟
reg 内存映射范围 确保地址对齐,避免TLB miss
status 设备启用/禁用 禁用未使用的设备,节省资源

举个例子。我在做一款工业控制板时,发现网卡吞吐量上不去。查了一圈,最后发现设备树里clock-frequency配的是50MHz,但硬件手册说可以跑到100MHz。改完之后,吞吐量直接翻倍。这种优化,不动一行C代码,只改设备树就行。

我的建议:做性能优化时,先看设备树。很多时候,性能瓶颈不是代码写得不好,而是硬件没配对。设备树就是硬件的“配置文件”,调好了,事半功倍。

1.5 本章小结

设备树不是什么神秘的东西。它就是一份硬件描述文件,让内核知道板子上有什么、怎么用。理解了这一点,后面的优化就好办了。

记住三个核心点:

  • 设备树是硬件与内核之间的“翻译官”
  • 设备树让硬件描述与内核代码解耦
  • 设备树里的参数直接影响性能