Linux设备树到底是啥?一张图看懂硬件适配的「翻译官」

烽融爱财 阅读:14640 2026-02-10

你有没有想过:同一份Linux内核镜像,为啥能在不同型号的开发板上跑起来?比如一块ARM架构的开发板,今天换个显示屏、明天加个传感器,内核不用重新编译就能识别新硬件——这背后,设备树(Devicetree功不可没。

很多嵌入式工程师刚接触设备树时,总被节点”“属性”“绑定规范这些术语绕晕。其实设备树的本质特别简单:它就是硬件和内核之间的翻译官,把硬件的长相能力写成标准化的文件,让内核不用硬编码就能读懂硬件。

今天咱们用人话+流程图拆解设备树,从为什么需要它内核怎么用它,一次讲透核心逻辑。

一、先搞懂:没有设备树时,Linux有多

在设备树出现前,Linux适配硬件靠的是硬编码”——把硬件参数(比如串口地址、中断号)直接写进内核代码里。比如要支持一款新开发板,工程师得:

1.在内核中新增一个板级文件,写死该板子的所有硬件配置;

2.编译内核时选择对应板子的配置,生成专属镜像;

3.要是换个硬件(比如把串口从UART1换成UART2),就得修改代码、重新编译。

这种方式的痛点太明显了:一款硬件对应一个内核镜像,嵌入式厂商要维护几十上百个镜像,成本极高。

而设备树的出现,彻底解决了这个问题:它把硬件描述从内核中剥离出来,做成独立的DTB文件(设备树二进制文件)。内核启动时读取DTB,就能动态识别硬件——从此实现一个内核镜像适配N种硬件

二、设备树的核心:3层结构,像给硬件画家谱

设备树的结构特别像一棵硬件家谱,最核心的是3个概念:节点(Node属性(Property路径(Path。咱们用一个简单的例子看懂:

/* 设备树源码(DTS文件)示例 *// { //根节点:代表整个硬件系统 compatible ="ti,omap3-beagleboard","ti,omap3"; //属性:告诉内核这是哪款硬件  chosen { //子节点:专门存储运行时配置 bootargs ="console=ttyS0,115200"; //属性:内核命令行(指定串口控制台) }; soc { //子节点:代表SoC(系统级芯片) compatible ="simple-bus"; //属性:说明这是“简单内存映射总线”  uart0: serial@4806a000{ //子节点:串口设备(@后是基地址) compatible ="ti,omap3-uart"; //属性:告诉内核用什么驱动 reg = <0x4806a0000x1000>; //属性:地址范围(基地址+大小) interrupts = <72>; //属性:中断号 }; };};

简单理解:

节点:对应一个硬件模块(如根节点=整个系统、uart0 =串口),用节点名@地址命名(地址可选,用于区分同类型设备);

属性:描述硬件的具体参数,格式是=(值可以是字符串、数字、二进制),比如compatible设备兼容性标识reg内存/ IO地址

路径:像文件路径一样定位节点,比如串口节点的路径是/soc/uart0

记住一个关键原则:设备树只描述硬件有什么、参数是多少,不包含任何驱动逻辑——驱动靠匹配设备树属性来关联硬件。

三、内核怎么用设备树?3步流程+ 1张图讲透

设备树的生命周期从编译内核使用,分为3个关键阶段。咱们结合流程图,一步步看内核是如何通过设备树识别并控制硬件的。

第一步:设备树文件的变身(编译阶段)

工程师写的是DTS文件(设备树源码,人类可读),但内核只能识别DTB文件(设备树二进制,机器可读)。这个转换靠工具dtcDevice Tree Compiler)完成:

dtc -Idts -O dtb -o my_board.dtbmy_board.dts

最终生成的DTB文件,会和内核镜像一起放在开发板的启动分区(比如boot分区)。

第二步:启动时传递DTB(引导阶段)

开发板上电后,先运行引导程序(如U-Boot,引导程序做两件关键的事:

1.初始化硬件(比如内存、串口);

2.DTB文件加载到内存的指定地址,然后启动内核,并告诉内核“DTB在内存的哪里

这一步就像:引导程序把硬件家谱DTB)递给内核,说这是你要管理的硬件,先看看说明书

第三步:内核解析DTB,创建设备(内核初始化阶段)

这是最核心的阶段,内核通过3个关键步骤,把DTB中的硬件描述变成可操作的设备实例。咱们用流程图+通俗解释拆解:

wKgZO2kah4OAC_v2AAXcBkTz8b0911.png

咱们把每个阶段掰开揉碎讲:

阶段1:平台识别——内核先搞清楚我跑在哪个板子上

内核启动后,首先要确定自己跑在什么硬件上(比如是BeagleBoard还是树莓派),这一步靠根节点的compatible属性

比如根节点的compatible = "ti,omap3-beagleboard", "ti,omap3",这个属性是从具体到通用的列表:

第一个值“ti,omap3-beagleboard”:精确匹配“TIomap3系列BeagleBoard开发板

第二个值“ti,omap3”:兼容“TIomap3系列所有板子

内核会遍历自己的平台描述库,找到和compatible最匹配的项——比如找到BeagleBoard的初始化逻辑,就执行对应的硬件初始化(如设置时钟电源)。

阶段2:运行时配置——内核获取启动参数

设备树中的/chosen节点是专门给内核传参数的通道,最常用的是bootargs属性(内核命令行)。

比如bootargs = "console=ttyS0,115200 loglevel=8",意思是:

console=ttyS0,115200:把串口0UART0)作为控制台,波特率115200

loglevel=8:显示所有级别的内核日志(方便调试)。

内核会解析这些参数,完成基础配置——比如初始化串口控制台,让开发者能通过串口看到内核启动日志。

阶段3:创建设备——内核把硬件描述变成可操作设备

这是设备树的最终目的:内核根据DTB中的节点,动态创建设备实例,再让驱动去匹配这些设备。

关键函数是of_platform_populate(),它的逻辑很简单:

1.从指定节点(默认是根节点)开始,遍历所有子节点;

2.对每个包含compatible属性的节点,创建一个平台设备platform_device);

3.驱动通过of_match_table(设备树匹配表),根据compatible属性找到对应的设备,完成驱动-设备绑定。

举个例子:串口节点uart0compatible = "ti,omap3-uart",内核会:

创建一个名为serial@4806a000的平台设备;

串口驱动的of_match_table中,正好有“ti,omap3-uart”这一项,于是驱动和设备绑定;

绑定后,驱动就能通过设备树中的reg(地址)、interrupts(中断号),控制串口硬件收发数据。

四、记住3个关键问题,避免踩坑

1.设备树能替代驱动吗?

不能!设备树只描述硬件参数,驱动才是控制硬件的大脑。比如设备树告诉内核串口在0x4806a000地址,但怎么发数据、收数据,还得靠串口驱动实现。

2.compatible属性写错了会怎样?

驱动找不到设备!比如把“ti,omap3-uart”写成“ti,omap4-uart”,串口驱动的匹配表中没有这个值,设备就会处于未绑定状态,无法使用。

3.DTB文件放错位置会怎样?

内核启动失败!引导程序如果没加载DTB,或者内核没找到DTB,会报Cannot find device tree”错误,然后卡住——因为内核不知道自己要管理什么硬件。

五、总结:设备树的本质是硬件标准化描述

其实设备树的核心价值,就在于标准化

对硬件厂商:按规范写DTS,不用改内核代码;

对内核开发者:按规范写驱动,不用适配每款硬件;

对嵌入式工程师:换硬件只换DTB,不用重新编译内核。

记住一句话:设备树是硬件的说明书,驱动是读懂说明书并操作硬件的人——两者配合,才能让Linux在千变万化的硬件上跑起来。

如果看完还是有点晕,建议找一款简单的开发板,打开它的DTS文件,对照本文的流程逐行看:根节点的compatiblechosen节点的bootargs、外设节点的reginterrupts——慢慢就会发现,设备树其实没那么复杂~

本文 zblog模板 原创,转载保留链接!网址:https://www.wbaas.cn/fengrong/1329.html

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

排行榜