想象你刚买下一套二手房。交房那天,物业递给你一本厚厚的《房屋使用手册》:里面有户型图、电路走向图、水管分布图,还有每间房的设施清单、门牌号、配电箱编号。有了这本手册,你不需要敲墙勘测、不需要打电话回访开发商,翻开手册,一目了然。早期 ARM Linux 世界里,恰恰没有这本手册——每一块开发板的硬件信息,都被维护者“背”在脑里,写进内核代码的板级文件中。新开发板一登场,就得修改内核源码、提交补丁、等待合并,周期长且极易冲突。后来,工程师们从 PowerPC 借鉴了设备树(Device Tree):把硬件描述从内核代码中剥离出来,变成一份独立的数据文件。系统启动时,固件把这份“硬件户型图”交到内核手里,内核照着地图接管整个板卡。本篇文章将带你从零认识设备树:它为什么诞生、长什么样、如何编译、如何参与启动、驱动如何匹配,以及遇到问题怎么排查。
掌握设备树是嵌入式与驱动开发的必备基本功。它能帮你读懂启动日志、快速定位外设无法工作的根因,是每位内核与裸机开发工程师的“入场券”。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
起源与动机
《房屋使用手册》不是一开始就有的。早年的物业公司里,每位老管家都把整栋楼的电路图、水管图记在自己脑子里。一旦老管家离职,新物业就抓瞎了。ARM Linux 早期的“老管家”,就是内核源码树里成堆的板级文件(board file):每个开发板都要有专属的 arch/arm/mach-xxx 目录,用 C 代码硬编码描述硬件。于是每推出一块新开发板,就要往内核里塞进一大坨代码。
这种用“代码写硬件”的方式带来了连锁难题:板子多了以后,内核补丁横飞,同一个驱动要为不同板子写不同的初始化分支。社区维护者疲于奔命,上游合并缓慢。设备树(Device Tree)正是从 PowerPC 的 Open Firmware 实践中借鉴来的“标准手册”:它规定,硬件信息不再用代码表达,而是写成一份纯粹的数据文件,跟着板卡走,内核只负责“读手册”。
1. 开发板标识:$\text{board}_i$ 表示系统中的第 $i$ 块开发板,每块板的硬件配置都不同。
2. 旧方案箭头:旧方式下,这块板子的硬件信息靠修改内核源码来表达。
3. 专属内核:$\text{kernel}_i$ 表示因板子而产生的内核分支,互相难以复用。
4. 新方案箭头:新方式下,硬件信息被抽离为独立数据,内核不再感知具体板子。
5. 设备树文件:$\text{DTS}_i$ 是描述该板子的设备树源文件,轻量且独立。
设备树的价值,正如物业管理行业的规范:手册一旦成为标准,任何一家物业公司(内核)都能在接钥匙后快速接管任何一栋楼(板卡)。这套“先定契约、再谈接管”的思路,奠定了后续所有章节的基础。
基本结构
你翻开《房屋使用手册》,会发现它不是一本流水账,而是一棵倒挂的“知识树”:第一页是整栋楼的总纲(根节点),下面分出“电气系统”“给排水系统”“网络布线”等大章节,每章再细分到楼层、房间、具体插座。设备树的数据结构正是这样一棵树——每个硬件都是一个“节点”,节点里挂着一串“属性”。
DTS(Device Tree Source)是人类可读的源文件格式,长得像 C 语言,却没有任何执行逻辑。根节点用 / {} 表示,下面可以挂 cpus、memory、soc 等子节点;子节点下还能继续挂孙节点。属性就是键值对,比如给某个串口节点写上 compatible = "acme,uart",就相当于在房间门口贴了一张铭牌。此外,DTS 还允许给节点加“标签”(label),方便其他节点引用——如同手册里给房间编号,水电工一看编号就知道该去哪间房。
1. 可选标签:$\text{label}?$ 表示标签可有可无,它像房间的别名,让其他节点能方便地引用本节点。
2. 节点名字:$\text{name}$ 是节点的正式名称,如 uart@1000,@ 后常跟地址以区分同类节点。
3. 节点体:$\{ \}$ 这对大括号圈定了节点的领地,所有内容都写在括号内。
4. 属性或子节点:$\text{property} \;|\; \text{node}$ 表示括号内装的要么是属性(键值对),要么是下级节点。
5. 任意多次重复:$^*$ 意为前面的内容可以出现零次或多次,所以一个节点的内容可多可少。
理解了节点与属性,设备树就不再神秘:整棵树就是对一台设备“自顶向下”的目录式描述。就像看手册找房间一样,内核按照树形路径一路下钻,就能精确定位任何一个硬件的全部信息。
编译与工具
《房屋使用手册》有两种形态:一种是给业主看的印刷排版稿(DTS),另一种是物业扫描枪一秒钟就能识别的二维码(DTB)。印刷稿人可以读,机器读起来费劲;二维码机器扫得快,人眼却看不懂。设备树同样有两种形态,由 DTC(Device Tree Compiler)这台“排版印刷机”完成转换。
DTS 经过 dtc 编译后,产出 DTB(Device Tree Blob)二进制文件。DTB 体积小、解析快,正是启动时内核真正消费的格式。反过来,当我们手里只有 DTB、想看内容时,可以用 fdtdump 或 dtc -I dtb -O dts 反编译,把二维码“翻译”回人眼的排版稿。在内核构建系统中,.dts 文件会被自动编译成 .dtb,烧写到启动分区的 dtb 区域——手册与二维码随房子一起交付。
1. 源文件:$\text{DTS}$ 是设备树的源文本,人可读、可注释、可维护。
2. 编译动作:$\xrightarrow{\;dtc\;}$ 表示 DTC 编译器执行格式转换,把文本变二进制。
3. 二进制产物:$\text{DTB}$ 是树形结构打包后的二进制块,启动时由引导程序加载。
4. 反编译:$\xrightarrow{\;fdtdump\;}$ 表示用反编译工具把二进制重新展开成文本。
5. 近似还原:$\text{DTS}' \approx \text{DTS}$ 说明反编译结果与原稿高度一致,但注释等信息可能丢失。
工欲善其事,必先利其器。掌握了 DTC 与反编译工具,你就同时拥有了“读手册”和“验真伪”的能力:拿到一块陌生板子,先反编译它的 DTB,看看这本手册到底写了什么,一切硬件布局尽收眼底。
启动流程
交房那天有一场“仪式”:开发商把钥匙和《房屋使用手册》一起放在茶几上,并提前告诉物业“手册放在进门左手边第二个抽屉”。ARM 启动流程也有类似约定:固件先把 DTB 加载进内存,然后把 DTB 的物理地址放进内核约定的寄存器——ARM 平台上这个寄存器是 r2,内核一启动就直奔 r2 取手册。
完整链路是这样的:芯片上电后由固化 ROM 引导,加载 U-Boot 等引导程序;U-Boot 初始化内存,把 DTB 从存储介质读到内存某地址,再用 bootz/bootm 启动内核,同时保证 r2 指向 DTB。内核入口处拿到地址后,先做“展平树”解析(unflatten),把紧凑的二进制结构还原成一棵内存中的节点树,随后挂载到内核的设备模型上。至此,硬件清单才真正变成内核看得懂的“活目录”。
1. 片上固件:$\text{固件}$ 是芯片出厂自带的 ROM 程序,负责第一段引导。
2. 引导程序:$\text{U-Boot}$ 是嵌入式最常用的引导程序,负责加载内核与 DTB。
3. 地址约定:$r_2 = \text{addr}(\text{DTB})$ 表示把 DTB 在内存中的物理地址写入 r2 寄存器,这是双方的握手约定。
4. 内核接棒:$\text{Kernel}$ 启动后第一时间从 r2 读取地址并校验。
5. 展平解析:$\text{unflatten}(\cdot)$ 把扁平二进制还原成树状结构,供驱动模型使用。
这套“固定位置交钥匙”的仪式保证了任何引导程序与任何内核之间都能默契配合。正如物业不必打电话问开发商,内核也不必猜硬件长什么样——寄存器里那只“手”,已经稳稳地把手册递了过来。
关键属性
手册里最实用的信息往往是几类固定的“标签”:门口铭牌说明房间用途,门牌号说明房间位置,应急指示说明报警器连到哪个总控台。设备树也有几个高频属性,理解了它们就理解了绝大多数 DTS 文件。
- compatible:格式为
"厂商,设备型号",如"arm,cortex-a9"。它是驱动匹配的“指纹”,内核靠它认出“这是谁家的什么设备”;还可以列出多个值,从最具体到最通用,像简历上的多个头衔。
- reg:描述设备的寄存器地址与长度,如
<0x1000 0x100>。它相当于门牌号加房间面积:告诉内核“从地址 0x1000 开始,占用 0x100 个字节”。地址和长度各占几个 32 位单元,由父节点的#address-cells与#size-cells决定。
- interrupts:描述中断号与触发方式,配合
interrupt-parent指定中断控制器。好比“烟雾报警器编号 29,接到总控台 1 号端口”。
- status:
"okay"表示设备启用,"disabled"表示保留但不启用。这如同手册里标注“此房间暂未交付”,内核便不会为它创建设备。
1. 兼容串:$\text{compatible}$ 是设备节点上的关键属性,驱动依据它来认领设备。
2. 厂商字段:$\text{vendor}$ 是厂商名,如 arm、acme,用于区分来源。
3. 设备型号:$\text{device}$ 是具体型号,如 cortex-a9,用于精确匹配。
4. 地址长度组:$\langle \text{addr}_0, \text{len}_0, \ldots \rangle$ 表示 reg 是一串“地址+长度”交替的元组,可描述多个寄存器段。
这些属性就像手册里约定俗成的“表格栏目”,人人遵守,内核才能高效解析。写设备树时,最忌讳的就是自创栏目名——内核看不懂、驱动配不上,最后只能对着启动日志抓耳挠腮。
驱动匹配
有了手册,物业公司还得有人能干对应工种:看到“施耐德配电箱”,得找到负责施耐德的电工师傅。Linux 驱动与设备节点的配对过程,就是一次“按铭牌找人”的匹配。内核维护一张大大的“员工花名册”,每个驱动都登记自己擅长处理的 compatible 串,这张表在驱动代码里就叫 of_match_table。
当内核遍历设备树时,每个节点会被转换成平台设备(platform device),然后总线把这些设备与驱动一一配对。配对的首要依据是 compatible 属性:逐一比对设备节点的兼容串与驱动表中登记的串,命中即建立绑定关系,并调用驱动的 probe 函数,让驱动正式“上岗”。若 compatible 都没匹配上,内核还会退而求其次,用节点名称等老规则兜底,就像保安实在找不到电工师傅,先找个“万能工”顶着。
1. 匹配函数:$\mathcal{M}(\text{node})$ 表示对某个设备节点执行的匹配操作,返回命中的驱动集合。
2. 驱动全集:$d \in \mathcal{D}$ 表明候选范围是系统中所有驱动 $d$ 组成的集合 $\mathcal{D}$。
3. 驱动声明:$\text{compat}(d)$ 是驱动通过 of_match_table 声明的兼容串集合。
4. 节点声明:$\text{compat}(\text{node})$ 是设备节点上 compatible 属性声明的兼容串集合。
5. 命中条件:$\in$ 表示驱动声明与节点声明存在交集,即“对上暗号”。
匹配机制保证了“设备与驱动各说各话也能对上”,这正是设备树解耦的威力。驱动作者不需要知道板子长什么样,只需登记自己擅长的 compatible;板子作者也不需要迁就某个驱动,只需把兼容串写标准。一次握手,两边安心。
设备树覆盖
房子住久了,你打算加装智能门锁。难道要把整本《房屋使用手册》重写一遍?当然不——物业允许你在手册上贴一张“补充说明”:写出新增设备的节点、改名或修正确认过的属性即可。设备树覆盖(Device Tree Overlay)干的就是这件事。
覆盖机制把“基础树”和“补丁片段”分开管理:基础 DTB 描述板卡出厂时的硬件,overlay 片段(编译产物为 .dtbo)描述运行时追加的硬件。启动后,用户在系统里通过 configfs 或 fdtoverlay 工具把 .dtbo 加载进去,内核在内存中把片段合并进现有设备树,新节点立即被扫描、匹配驱动、创建设备。整个过程不需要重新编译内核,也不需要重新烧写 DTB 分区。
1. 基础树:$T$ 是板卡出厂自带的设备树,描述基本硬件。
2. 覆盖片:$O$ 是记录增量修改的覆盖片段,例如新增一个 GPIO 扩展器节点。
3. 叠加运算:$\oplus$ 表示把覆盖片中的节点插入或替换到基础树的对应位置,不是简单拼接。
4. 合并结果:$T'$ 是合并后的新树,后续驱动匹配都基于这棵新树进行。
覆盖机制让“房型升级”变得像贴便签一样轻巧。在 FPGA 动态配置、树莓派扩展板(HAT)等场景中,硬件能力可以在运行期被动态“点亮”。手册与贴纸并存,既保住了出厂信息,又拥抱了无限扩展。
调试与排错
装修完入住,你发现客厅插座没电。正常业主的做法是:翻手册找到该插座对应的回路编号,去电闸箱确认空气开关有没有合上,再用万用表量一量线路。设备树调试也是同一套逻辑:先看“手册”(DTS/DTB 预期内容),再看“实际接线”(内核运行时视图),最后用“仪表”(日志与属性文件)逐段定位。
内核启动后,会在 /proc/device-tree/ 下按节点路径导出设备树视图,每个节点是一个目录,每个属性是一个文件,用 ls 和 cat 就能直接读取。而“手册原件”则通过 fdtdump 查看 DTB 的原始内容。两者对照,再加上启动日志中驱动报错信息,就能找到问题归属:是 compatible 拼写不一致?reg 地址写错?status 忘了改成 "okay"?还是中断号对不上?排查思路永远是一层层比对:理想状态是什么,实际状态是什么,中间哪一步出了偏差。
1. 设计意图:$\text{DTS}_{\text{意图}}$ 是开发者在源文件里写的理想描述。
2. 编译成型:$\xrightarrow{dtc}$ 表示源码经编译器变成 DTB,语法错误在此暴露。
3. 实际二进制:$\text{DTB}_{\text{实际}}$ 是烧写入系统、被引导程序加载的真实数据。
4. 内核落地:$\xrightarrow{unflatten}$ 表示内核启动时把二进制还原成内存树。
5. 运行时视图:$\text{proc}_{\text{视图}}$ 是 /proc/device-tree 下内核实际暴露的节点与属性。
调试设备树没有玄学,只有“三份对照”:意图、实际、视图。哪一份对不上,就往哪一份的方向深挖。正如老电工常说的:“手册不会骗人,骗人的是你没看懂手册。”
AI 应用场景
SoC 板卡 Bring-up 阶段
阶段:硬件抽象与设备接入阶段
解决的问题:AI 推理板卡(如 NPU 加速卡)接入新主控平台时,传统方式需要反复修改内核源码适配不同芯片,周期长、风险高。设备树用数据描述硬件,使得“换平台”变成“换手册”——只需在新系统上写好 DTS 并编译成 DTB,内核无需改动即可识别板卡上的 NPU、内存、串口。
具体案例:某团队将自研 NPU 加速卡从 i.MX8 平台移植到 RK3588 平台。工程师根据新平台手册编写 DTS,描述 PCIe 枚举出的地址窗口与中断映射,编译 DTB 后与 U-Boot 一起烧写,内核启动即识别设备并加载驱动,整体适配时间从两周缩短到两天。
边缘设备外设驱动适配
阶段:数据采集与传感器接入阶段
解决的问题:边缘 AI 设备常搭配摄像头、激光雷达、温湿度传感器等外设。传统方式下每换一个传感器就要改驱动代码;设备树方式下,驱动只需声明自己支持的 compatible 串,传感器的型号、地址、中断在 DTS 中配置即可,硬件更换不再触碰代码。
具体案例:一辆无人小车的视觉系统需要把摄像头从 OV5640 换成 IMX219。工程师在 DTS 中修改 I2C 总线下的摄像头节点 compatible 与复位 GPIO,重新编译 DTB 后重启,新摄像头被自动枚举并匹配到驱动,图像采集链路立即恢复。
设备树覆盖用于产品扩展
阶段:产品量产与运维扩展阶段
解决的问题:AI 盒子类产品往往提供扩展接口(USB、SPI、GPIO),用户插入不同扩展板就需要不同的硬件配置。传统方式需要厂家发布新固件;设备树覆盖机制允许把每款扩展板的描述做成一个 .dtbo,运行时按需加载,实现“插入即用”。
具体案例:某 AI 边缘网关支持用户选配 4G 模块或 CAN 总线板。厂家随产品提供两个 .dtbo 文件,用户在系统里通过 configfs 加载对应覆盖片,内核动态追加节点并创建设备,无需重启或刷机。
Python 演示
#!/usr/bin/env python3"""极简设备树解析器:解析 DTS 缩进文本,构建节点树,支持按路径查找与 compatible 匹配。""" class DeviceNode: def __init__(self, name): self.name = name self.props = {} self.children = [] def find(self, path): node = self for part in path: target = None for child in node.children: if child.name == part: target = child break if target is None: return None node = target return node def match(self, compatible): if compatible in self.props.get('compatible', ''): return True return any(child.match(compatible) for child in self.children) def dump(self, indent=0): lines = [' ' * indent + self.name] for key, value in self.props.items(): lines.append(' ' * (indent + 1) + key + ' = ' + value) for child in self.children: lines.extend(child.dump(indent + 1)) return lines def parse_dts(text): root = None stack = [] for raw in text.splitlines(): line = raw.strip() if not line: continue if line == '};': if stack: stack.pop() elif '=' in line and line.endswith(';'): if stack: key, value = line.rstrip(';').split('=', 1) stack[-1].props[key.strip()] = value.strip().strip('"') elif line.endswith('{'): name = line.rstrip('{').strip() if name == '/': name = 'root' node = DeviceNode(name) if stack: stack[-1].children.append(node) else: root = node stack.append(node) return root if __name__ == '__main__': sample = ''' / { compatible = "acme,board-v1"; cpus { cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; }; }; serial@1000 { compatible = "acme,uart"; reg = <0x1000 0x100>; }; }; ''' tree = parse_dts(sample) print('\n'.join(tree.dump())) node = tree.find(['serial@1000']) print('find serial@1000:', node.name if node else 'None') print('match acme,uart:', tree.match('acme,uart')) print('match acme,gpio:', tree.match('acme,gpio'))
class DeviceNode:定义节点类,节点是设备树的骨架,相当于“房间”。self.children = []用列表保存子节点,构建出树形层级。def find(self, path):按路径逐级下钻查找节点,类似在/proc/device-tree里找设备目录。def match(self, compatible):深度优先搜索,检查自身或任意子树是否声明了该兼容串。self.props.get('compatible', '')从属性字典中取 compatible 值,没有则为空串,对应内核读取设备树属性。if compatible in ...判断驱动登记的兼容串是否命中节点声明,正是内核匹配驱动的基本操作。elif '=' in line and line.endswith(';'):识别属性行并解析键值对,对应 DTC 编译时的语法解析。elif line.endswith('{'):识别节点开始行并压栈,构造父子关系。tree.match('acme,uart')返回True,模拟驱动与串口节点成功“握手”。tree.match('acme,gpio')返回False,说明没有任何节点声明该兼容串,驱动不会上岗。
常见误区
误区“设备树里写了 hardware 的信息,那它是不是就是驱动?我把 DTS 改对,外设就能工作了吧?”
正解设备树只是数据,不是代码,更不是驱动。它好比房屋手册,手册画得再详细,也不会自己动手修电路——真正干活的是电工(驱动)。设备树负责告诉内核“这里有个什么设备、地址在哪”,驱动负责实际操控硬件。两者是“图纸”与“施工队”的关系,缺一不可。
为何常见:DTS 语法带大括号、分号,长得像 C 语言,初学者容易误以为它是可执行程序。
误区“设备树文件放在内核源码目录里,所以改了它要重新 make 内核,再刷整个内核镜像才生效。”
正解DTS 只是被内核构建系统顺手编译成 DTB,它本质上是独立数据文件。多数平台只需重新编译 DTB 并烧写 dtb 分区(或打包进 boot 镜像),内核本体无需重编。改设备树与改内核代码是两码事,正如重印手册不必重建房子。
为何常见:设备树源码确实位于内核源码树内(arch/arm/boot/dts/),路径上的“亲缘关系”误导了直觉。
误区“compatible 不就是个字符串吗?我随便拼一个,驱动那边也拼一样的,不就能匹配了?”
正解compatible 有严格的 "厂商,型号" 规范,并承载“从具体到通用”的回退语义。乱写会导致:内核日志里没有驱动认领设备,社区代码无法复用,上游维护者无法 review。正确的做法是沿用厂商前缀与标准命名,如 "arm,cortex-a9",必要时附加多个回退值。
为何常见:字符串看起来没有类型约束,容易让人忽视其背后承担的“命名空间”职责。
误区“设备树描述一切硬件,所以 USB 鼠标、PCIe 显卡、内存条型号都要在 DTS 里写清楚。”
正解设备树主要描述“不可自描述”的总线上挂载的设备,如内存映射外设、I2C/SPI 设备。USB、PCIe 这类总线支持枚举(即插即用),设备会被自动发现,不需要也不应该在设备树里逐个描述。内存的大小也由引导程序动态修改 DTB 或由内核探测,而不是写死。
为何常见:初学者看到 DTS 里写了一大堆节点,容易以偏概全,以为所有硬件都要“点名”。
面试题
Q1. 设备树 DTS 源文件经过 DTC 编译后的产物是什么?
- A.可执行 ELF 映像文件
- B.DTB 设备树二进制← 正确答案
- C.设备树源文本文件
- D.内核镜像压缩文件
答案:B。DTC 的作用正是把人类可读的 DTS 文本编译成机器高效的 DTB 二进制。DTB 会被引导程序加载并在启动时交给内核,而不是可执行程序。选项 A、D 是内核或程序产物,C 是输入而非输出。
Q2. 设备树中承载硬件信息的基本单位是什么?
- A.属性的键值对
- B.可执行指令
- C.节点与子节点← 正确答案
- D.内存地址段
答案:C。设备树是一棵层级树,节点是基本单位,每个节点代表一个设备或总线;属性则挂在节点下描述细节。指令是程序的单位,地址段只是 reg 属性的一部分内容,均非基本单位。
Q3. 关于设备树的 compatible 属性,下列说法正确的有哪些?
- A.采用厂商与型号格式← 正确答案
- B.驱动匹配的核心依据← 正确答案
- C.用于描述内存大小
- D.可提供多个备选值← 正确答案
答案:ABD。compatible 的规范格式是 "厂商,型号",内核驱动通过它来认领设备,且可以列出多个值作为从具体到通用的回退。内存大小由 reg 或引导程序管理,与 compatible 无关,因此 C 错误。
Q4. ARM Linux 启动时,内核从哪个寄存器获取 DTB 的物理地址?
- A.r0 寄存器
- B.r1 寄存器
- C.r2 寄存器← 正确答案
- D.sp 寄存器
答案:C。ARM 启动约定规定 r0 为 0、r1 为机器号(部分平台已不用)、r2 存放 DTB 物理地址。这是引导程序与内核之间的“交钥匙”契约,sp 与栈相关,与传参无关。
Q5. 将 DTB 反编译为 DTS 以便阅读的命令是哪一个?
- A.dtc -I dtb -O dts← 正确答案
- B.dtc -I dts -O dtb
- C.gcc -I dtb -O dts
- D.objcopy -I dtb -O dts
答案:A。dtc 的 -I 指定输入格式、-O 指定输出格式,因此 -I dtb -O dts 实现二进制到文本的反编译。B 方向相反,是正向编译;C、D 使用了错误工具,gcc 和 objcopy 不处理设备树格式。
Q6. 关于内核中驱动与设备节点的匹配,下列说法正确的有哪些?
- A.compatible 属性优先级最高← 正确答案
- B.无匹配时可回退看节点名称← 正确答案
- C.匹配成功后调用 probe 函数← 正确答案
- D.匹配失败会导致内核崩溃
答案:ABC。内核优先用 compatible 匹配,兼容串全部失败后会按历史规则回退到设备类型和节点名称。匹配成功才创建绑定关系并调用 probe,驱动正式生效。匹配失败最多是设备“没人认领”、不创建设备,不会导致内核崩溃,D 错误。
Q7. 设备树覆盖(Device Tree Overlay)最典型的使用场景是什么?
- A.加快内核编译速度
- B.运行时扩展硬件描述← 正确答案
- C.替代驱动代码编写
- D.压缩内核日志输出
答案:B。覆盖机制把基础树与增量片段 .dtbo 在运行时合并,从而动态描述新接入的硬件,常见于扩展板、FPGA 动态配置。它不参与编译优化,也不替代驱动,更与日志无关。
Q8. 在运行中的 Linux 系统里,查看内核实际设备树视图的目录是哪个?
- A./proc/device-tree← 正确答案
- B./dev/device-tree
- C./sys/device-tree
- D./etc/device-tree
答案:A。内核展开设备树后,会在 /proc/device-tree/ 下以目录和文件形式导出节点与属性。/dev 是设备文件,/etc 是配置文件目录,/sys 下虽有固件相关信息,但设备树的标准视图在 /proc/device-tree。
Q9. 外设无法工作时,下列属于设备树排查手段的有哪些?
- A.fdtdump 反编译核对内容← 正确答案
- B.检查节点和 status 状态← 正确答案
- C.核对 compatible 拼写← 正确答案
- D.直接重装整个内核
答案:ABC。正确的排查逻辑是“手册、视图、日志”三层对照:用 fdtdump 看 DTB 原始内容,用 /proc/device-tree 看内核实际视图,再核对 compatible 与 status。直接重装内核属于无差别操作,既非排查手段也浪费时间。
Q10. 某设备节点 status 属性被设为 "disabled" 时,内核通常会怎样处理?
- A.仍创建对应设备
- B.保留节点不建设备← 正确答案
- C.忽略整个子树信息
- D.启动报错直接退出
答案:B。status 为 “disabled“ 的节点会保留在设备树中,但内核在把设备树转换成平台设备时会跳过它,因此设备不会出现。子树信息并不被粗暴丢弃——之后通过覆盖机制把 status 改为 ”okay” 仍可启用,这正是覆盖机制的用武之地。