【Linux驱动开发】第11天:设备树(Device Tree)超详细全解:从诞生背景到工作原理

一、设备树的诞生背景:传统驱动的致命痛点

在设备树出现之前(Linux 3.0之前),Linux内核采用硬编码的方式描述所有硬件信息。这意味着:

  • 每一个开发板的寄存器地址、中断号、GPIO号,都直接写死在驱动代码里
  • 换一个开发板,哪怕只是GPIO号变了,也要修改驱动代码重新编译
  • 为了支持不同的开发板,内核里充斥着大量重复的、针对特定硬件的代码

最直观的例子:ARM内核的"灾难"

ARM架构有上百种不同的芯片、上千种不同的开发板。在设备树出现之前:

  • 每一个开发板都需要在内核里添加一份自己的硬件描述代码
  • 内核代码量爆炸式增长,变得无比臃肿
  • 维护成本极高,每一个新开发板都需要内核开发者手动添加支持
  • 一个内核镜像只能支持一个开发板,无法做到"一个镜像跑遍所有开发板"

设备树的诞生

2011年,Linux 3.1内核正式引入设备树机制,彻底解决了这个问题

设备树的核心思想非常简单:

把硬件描述信息从内核代码中完全抽离出来,用一个独立的、通用的文件来描述硬件。内核只需要一份通用的驱动代码,通过读取这个文件来识别不同的硬件。


二、设备树的核心定义

设备树(Device Tree,简称DT) 是一种描述硬件资源的数据结构,它用树形结构来描述一个计算机系统的所有硬件设备,包括:

  • CPU
  • 内存
  • 总线控制器(I2C、SPI、USB等)
  • 外设(LED、按键、传感器、显示屏等)
  • 中断、时钟、电源等硬件资源

设备树的本质

设备树本质上是一个硬件的"清单",它告诉内核:

这个板子上有哪些硬件?它们的地址在哪里?它们用了哪个中断?它们的工作频率是多少?

注意:设备树不是驱动! 它只描述"硬件是什么",不描述"怎么操作硬件"。操作硬件的逻辑仍然在驱动代码里。


三、设备树的三大核心组件:DTS、DTB、DTC

设备树体系由三个核心部分组成,三者分工明确,缺一不可。

1. DTS:设备树源文件(Device Tree Source)

  • 文件后缀.dts(主文件)、.dtsi(头文件)
  • 文件类型:纯文本文件,人类可读可编辑
  • 作用:用C语言风格的语法,描述硬件的所有信息
  • 编辑方式:任何文本编辑器都可以编辑
什么是.dtsi文件?

.dtsi是设备树头文件,相当于C语言的.h头文件,用于存放多个DTS文件共享的公共硬件信息

  • 比如同一个芯片的所有开发板,芯片内部的外设(CPU、内存、UART、I2C控制器等)是完全相同的
  • 这些公共信息可以放在一个.dtsi文件中(如stm32mp157.dtsi
  • 每个开发板自己的.dts文件只需要#include这个.dtsi,然后添加自己独有的硬件信息(如LED、按键、外接传感器等)
  • 大大提高了代码复用性,减少了重复代码

2. DTC:设备树编译器(Device Tree Compiler)

  • 文件类型:可执行程序
  • 作用:将人类可读的DTS文本文件,编译成内核能够识别和解析的二进制DTB文件
  • 同时也可以将DTB二进制文件反编译成DTS文本文件,用于调试

3. DTB:设备树二进制文件(Device Tree Blob)

  • 文件后缀.dtb
  • 文件类型:二进制文件,人类不可读
  • 作用:内核启动时加载并解析的最终文件
  • 特点:体积小、解析速度快,适合嵌入式系统使用

三者的关系和编译流程

开发者编写DTS源文件
#include 公共.dtsi头文件
DTC编译器编译
生成DTB二进制文件
烧录到开发板
bootloader加载DTB到内存
内核解析DTB,识别硬件


四、设备树的树形结构

设备树采用树形结构来描述硬件,和计算机的文件系统结构非常相似。

4.1 基本结构

复制代码
/ (根节点)
├── cpu@0 (CPU节点)
├── memory@80000000 (内存节点)
├── soc@0 (片上系统节点)
│   ├── uart@12340000 (串口节点)
│   ├── i2c@12341000 (I2C控制器节点)
│   │   ├── sensor@48 (I2C传感器节点)
│   │   └── eeprom@50 (I2C EEPROM节点)
│   └── gpio@12342000 (GPIO控制器节点)
└── led@12343000 (LED节点)

4.2 核心概念:节点(Node)

  • 每个硬件设备对应设备树中的一个节点
  • 节点格式:节点名@地址 { ... };
    • 节点名 :描述设备的类型,如cpumemoryuartled
    • @地址 :设备的寄存器基地址,用于区分同类型的不同设备(如uart@12340000uart@12341000是两个不同的串口)

4.3 核心概念:属性(Property)

  • 每个节点包含多个属性,用于描述设备的具体信息
  • 属性格式:属性名 = 属性值;
  • 属性值可以是字符串、32位无符号整数、整数数组、字节数组等

4.4 最常用的核心属性(驱动开发每天都会用到)

属性名 作用 示例
compatible 最重要的属性 ,用于和驱动匹配,格式为"厂商,设备名" compatible = "st,stm32-uart";
reg 描述设备的寄存器地址范围,格式为<基地址 长度> reg = <0x12340000 0x1000>;
interrupts 描述设备使用的中断号 interrupts = <5>;
status 描述设备状态,okay表示启用,disabled表示禁用 status = "okay";
model 设备的人类可读名称 model = "STM32MP157 Development Board";

五、设备树的完整工作流程

设备树从编写到最终驱动硬件,会经历以下6个完整阶段。

阶段1:编写设备树源文件

开发者根据开发板的硬件原理图,编写DTS文件,描述所有硬件信息。

阶段2:编译生成DTB文件

使用DTC编译器将DTS文件编译成DTB二进制文件。

阶段3:bootloader加载DTB

系统启动时,bootloader(如U-Boot)会:

  1. 将内核镜像(zImage)加载到内存
  2. 将DTB文件加载到内存的另一个位置
  3. 启动内核,并将DTB的内存地址传递给内核

阶段4:内核解析DTB

内核启动时,根据bootloader传递的地址,解析DTB文件:

  1. 遍历DTB中的所有节点
  2. 为每个节点创建一个对应的platform_device结构体
  3. platform_device注册到platform总线

阶段5:总线匹配设备和驱动

platform总线会:

  1. 遍历所有已注册的platform_driver
  2. 比较设备的compatible属性和驱动的of_device_id
  3. 如果字符串完全一致,则匹配成功

阶段6:调用驱动的probe函数

匹配成功后,总线自动调用驱动的probe函数:

  1. 驱动从platform_device中获取硬件信息(地址、中断号等)
  2. 初始化硬件
  3. 注册字符设备/块设备/网络设备
  4. 创建设备文件,对外提供服务

六、设备树的核心优势

对比传统硬编码驱动,设备树具有以下不可替代的优势:

1. 硬件描述与驱动代码完全分离

  • 驱动代码通用,不需要针对特定开发板修改
  • 一个驱动可以支持所有符合compatible属性的设备
  • 新增硬件只需要修改设备树,不需要修改驱动代码

2. 一个内核镜像支持多个开发板

  • 内核镜像不再包含任何特定硬件的信息
  • 同一个内核镜像可以在所有支持设备树的开发板上运行
  • 只需要更换不同的DTB文件即可

3. 大大降低内核维护成本

  • 内核不再需要包含大量针对特定开发板的硬编码代码
  • 内核代码量大幅减少,更加简洁和通用
  • 新开发板的支持变得非常简单,只需要添加一个DTS文件

4. 支持热插拔

  • 设备树可以动态描述热插拔设备的信息
  • 设备插入时,内核动态解析设备树节点,创建设备并匹配驱动
  • 设备拔出时,自动调用驱动的remove函数释放资源

七、常见误区澄清

❌ 误区1:设备树可以代替驱动

错误。设备树只描述"硬件是什么",不描述"怎么操作硬件"。操作硬件的逻辑仍然在驱动代码里。没有驱动,设备树只是一个没用的文本文件。

❌ 误区2:设备树是ARM架构特有的

错误。设备树最早用于PowerPC架构,现在已经被所有主流架构采用,包括ARM、x86、RISC-V、MIPS等。

❌ 误区3:设备树只能描述片上外设

错误。设备树可以描述系统中的所有硬件,包括CPU、内存、总线控制器、外接设备、甚至电源和时钟。

❌ 误区4:设备树的compatible属性可以随便写

错误compatible属性必须严格按照"厂商,设备名"的格式编写,并且必须和驱动中的of_device_id表完全一致,否则无法匹配。


八、总结

设备树是现代Linux驱动开发的基础,它的核心价值在于实现了硬件描述与驱动代码的彻底分离,解决了传统硬编码驱动的所有致命痛点。

对于驱动开发者来说,你只需要记住:

  1. 设备树是硬件的清单,告诉内核有什么硬件
  2. 驱动是硬件的操作手册,告诉内核怎么操作硬件
  3. compatible属性是两者之间的配对暗号,暗号对上了,驱动才能工作

面试必背考点

  1. 什么是设备树?它解决了什么问题?
  2. DTS、DTB、DTC分别是什么?三者的关系是什么?
  3. 什么是.dtsi文件?它的作用是什么?
  4. 设备树的基本结构是什么?什么是节点和属性?
  5. compatible属性的作用是什么?格式是什么?
  6. 内核解析设备树的完整流程是什么?
  7. bootloader在设备树启动过程中起到什么作用?
  8. 设备树和platform驱动是怎么配合工作的?
相关推荐
七歌杜金房3 小时前
我终于又有了自己的 Linux 电脑
linux·debian·mac
SkyWalking中文站11 小时前
认识 Horizon UI · 5/17:3D 基础设施地图
运维·监控·自动化运维
tntxia1 天前
linux curl命令详解_curl详解
linux
扛枪的书生1 天前
Linux 网络管理器用法速查
linux
SkyWalking中文站1 天前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控
顺风尿一寸1 天前
Java Socket 内核之旅:从 SocketChannel.read() 到 tcp_recvmsg 与 epoll 的完整调用链路
linux
雪梨酱QAQ1 天前
Kubeneters HA Cluster部署
运维
江华森2 天前
Spring Cloud 微服务全栈实战:从 Eureka 到 Docker Compose 一文贯通
运维
江华森2 天前
Matplotlib 数据绘图基础入门
运维
XIAOHEZIcode2 天前
Ubuntu 终端美化全栈指南:Bash 到 Kitty 踩坑实录
linux·ubuntu·命令行