2. 设备树基础回顾:DTB/DTS格式、节点与属性、compatible与reg的语义
设备树(Device Tree)是嵌入式Linux系统中用于描述硬件拓扑结构的关键机制。它从Bootloader传递到内核,替代了传统平台代码中硬编码的硬件描述。理解其格式、节点与属性的语义,是后续解析性能优化的前提。
2.1 DTS与DTB:源文件与二进制镜像
设备树的开发流程涉及两种文件格式:DTS(Device Tree Source)和DTB(Device Tree Blob)。DTS是文本格式的源文件,由开发者编写;DTB是二进制格式的编译产物,由Bootloader加载并传递给内核。
| 特性 | DTS(源文件) | DTB(二进制镜像) |
|---|---|---|
| 格式 | 文本(.dts / .dtsi) | 二进制(.dtb) |
| 可读性 | 人类可读,便于编辑 | 机器可读,紧凑高效 |
| 编译工具 | dtc(Device Tree Compiler) | 由dtc生成 |
| 内核解析入口 | 不直接使用 | unflatten_device_tree() |
| 大小 | 较大(含注释、缩进) | 较小(去除冗余) |
性能提示:内核启动时解析的是DTB而非DTS。DTB的二进制结构决定了遍历效率,因此优化DTB布局(如节点排序、属性对齐)能直接减少解析时间。
以下是一个典型的DTS文件片段,描述了一个UART控制器:
/dts-v1/;
/ {
model = "Example Board v1";
compatible = "vendor,example-board";
soc {
uart0: serial@10000000 {
compatible = "vendor,uart-ip-v2";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
clock-frequency = <24000000>;
status = "okay";
};
};
};
2.2 节点(Node)与属性(Property)的结构
设备树的核心结构由节点和属性组成。每个节点代表一个硬件设备或总线,属性则描述该设备的配置参数。
- 节点(Node):以
node-name@unit-address形式命名,可嵌套形成树形结构。根节点为/。 - 属性(Property):键值对,值可以是字符串、整数数组、字节数组或
phandle(引用其他节点)。
关键区别:节点是“硬件实体”的抽象,属性是“配置参数”的载体。解析性能优化的核心在于减少节点遍历次数和属性查找时间。
下表列出了常见属性及其语义:
| 属性名 | 值类型 | 语义 |
|---|---|---|
compatible |
字符串列表 | 设备驱动匹配标识 |
reg |
整数数组 | 地址/长度对,描述寄存器空间 |
interrupts |
整数数组 | 中断号、触发类型 |
status |
字符串 | 设备启用状态("okay"/"disabled") |
clocks |
phandle列表 | 时钟源引用 |
2.3 compatible属性的语义与匹配机制
compatible是设备树中最关键的属性,它建立了硬件设备与内核驱动之间的绑定关系。其值为一个字符串列表,按优先级从高到低排列。
- 格式:
"厂商,设备型号",例如"vendor,uart-ip-v2"。 - 匹配优先级:列表中的第一个字符串优先级最高,内核依次尝试匹配。
- 通配支持:可使用
"simple-bus"、"simple-mfd"等通用字符串实现批量匹配。
性能陷阱:如果compatible列表过长(超过5个字符串),内核在of_match_node()中会进行多次字符串比较,增加解析开销。建议将最精确的匹配项放在首位。
内核驱动匹配流程如下:
- 内核遍历设备树节点,调用
of_match_node()。 - 将节点的
compatible属性值与驱动注册的of_match_table逐一比较。 - 匹配成功则调用驱动的
probe()函数;失败则跳过该节点。
// 内核驱动中的 of_match_table 示例
static const struct of_device_id uart_of_match[] = {
{ .compatible = "vendor,uart-ip-v2", .data = &uart_v2_data },
{ .compatible = "vendor,uart-ip-v1", .data = &uart_v1_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uart_of_match);
2.4 reg属性的语义与地址映射
reg属性描述了设备在总线上的地址空间,通常由地址和长度组成。其格式由父节点的 #address-cells 和 #size-cells 决定。
| 父节点属性 | 含义 | 示例值 |
|---|---|---|
#address-cells |
地址字段占用的32位单元数 | 1(32位地址)或 2(64位地址) |
#size-cells |
长度字段占用的32位单元数 | 1(32位长度)或 0(无长度) |
例如,reg = <0x10000000 0x1000> 表示基地址为 0x10000000,长度为 0x1000(4KB)。内核通过 of_iomap() 将这段地址映射到虚拟内存空间。
优化要点:reg属性的解析涉及地址转换和资源分配。如果设备树中reg条目过多(如多段不连续内存),内核需要多次调用of_translate_address(),增加解析延迟。建议将连续地址合并为单一reg条目。
2.5 节点与属性的性能影响总结
设备树的解析性能直接受节点数量和属性查找方式影响。以下表格总结了常见性能瓶颈及优化方向:
| 瓶颈点 | 原因 | 优化方向 |
|---|---|---|
| 节点遍历 | 内核使用深度优先遍历,节点过多增加递归深度 | 合并冗余节点,减少层级 |
| 属性查找 | of_find_property() 线性扫描属性列表 | 将高频属性放在节点属性列表前端 |
| compatible匹配 | 字符串比较开销大,列表过长 | 精简compatible列表,优先精确匹配 |
| reg解析 | 地址转换涉及父节点链式查找 | 减少reg条目数量,使用连续地址 |
实践建议:在编写DTS时,尽量保持节点扁平化(减少嵌套深度),将常用属性(如compatible、reg)放在节点属性列表的前部。这些微调在大型SoC设备树中能显著提升解析速度。