Linux内核设备树节点查找实战:of_find_node_by_name全解析
【免费下载链接】linux Linux kernel source tree 项目地址: https://gitcode.com/GitHub_Trending/li/linux
1. 你是否还在为设备树节点查找抓狂?
设备树(Device Tree,DT)作为Linux内核中硬件描述的核心机制,其节点查找效率直接影响驱动初始化性能。当系统启动时,驱动需要在数百个设备节点中精准定位目标硬件,而of_find_node_by_name正是完成这一任务的关键函数。本文将系统讲解该函数的实现原理、使用场景与性能优化策略,帮助开发者彻底解决设备树节点查找难题。
读完本文你将掌握:
of_find_node_by_name的参数设计与返回值解析- 节点查找的内核实现流程与性能瓶颈
- 5种典型使用场景的代码实现
- 从O(n)到O(1)的查找性能优化方案
- 常见错误案例的调试与规避方法
2. 函数原型与核心数据结构
2.1 函数定义解析
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
参数说明:
from:起始查找节点(NULL表示从根节点开始)name:目标节点的name属性值(非full_name)
返回值:
- 成功:指向匹配节点的
struct device_node指针(需调用of_node_put释放引用) - 失败:NULL(表示未找到匹配节点)
2.2 核心数据结构
struct device_node {
const char *name; // 节点名称(不含单元地址)
const char *full_name; // 包含单元地址的完整名称
struct device_node *parent;// 父节点指针
struct device_node *child; // 第一个子节点指针
struct device_node *sibling;// 下一个兄弟节点指针
phandle phandle; // 节点句柄
struct property *properties; // 属性链表
// ... 其他字段
};
关键属性:
name:用于匹配的节点名称,如"gpio"、"cpus"full_name:包含单元地址的完整路径,如"gpio@12340000"- 链表结构:通过
child和sibling形成树状层次结构
3. 内核实现原理与性能分析
3.1 深度优先搜索实现
// 简化版实现逻辑
struct device_node *of_find_node_by_name(struct device_node *from, const char *name) {
struct device_node *np, *next;
// 如果from为NULL,从根节点开始搜索
np = from ? from->child : of_root;
for (; np; np = next) {
// 检查当前节点名称是否匹配
if (of_node_name_eq(np, name)) {
of_node_get(np); // 增加引用计数
return np;
}
// 递归搜索子节点
if ((next = of_find_node_by_name(np, name)))
return next;
// 移动到下一个兄弟节点
next = np->sibling;
// 如果到达兄弟节点链表末尾,回溯到父节点的兄弟
while (!next && np != from) {
np = np->parent;
next = np ? np->sibling : NULL;
}
}
return NULL;
}
3.2 性能复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 单次查找 | O(n) | O(d) | n:节点总数,d:树深度 |
| 最坏情况 | O(n²) | O(d) | 连续查找多个节点时累积 |
| 平均情况 | O(n) | O(log n) | 平衡树结构下的深度 |
性能瓶颈:
- 深度优先搜索(DFS)导致的线性遍历
- 无缓存机制,重复查找相同节点时重复遍历
- 节点名称比较使用字符串全匹配
4. 使用场景与代码实现
4.1 基础场景:从根节点查找指定名称节点
// 查找名为"cpus"的节点
struct device_node *cpus_node = of_find_node_by_name(NULL, "cpus");
if (cpus_node) {
printk("Found cpus node: %s\n", cpus_node->full_name);
of_node_put(cpus_node); // 释放节点引用
}
4.2 进阶场景:从指定节点开始查找
// 先找到I2C控制器节点,再在其下查找"eeprom"子节点
struct device_node *i2c_node = of_find_node_by_name(NULL, "i2c-controller");
if (i2c_node) {
struct device_node *eeprom_node = of_find_node_by_name(i2c_node, "eeprom");
if (eeprom_node) {
// 处理eeprom节点
of_node_put(eeprom_node);
}
of_node_put(i2c_node);
}
4.3 高级场景:遍历查找所有同名节点
// 查找系统中所有名为"led"的节点
struct device_node *prev = NULL;
while (1) {
struct device_node *led_node = of_find_node_by_name(prev, "led");
if (!led_node) break;
printk("Found LED node: %s\n", led_node->full_name);
prev = led_node; // 下次从当前节点继续查找
// 注意:此处不调用of_node_put,保持引用直到遍历结束
}
of_node_put(prev); // 最后释放引用
4.4 驱动场景:设备匹配与资源获取
static int gpio_driver_probe(struct platform_device *pdev) {
struct device_node *np = pdev->dev.of_node;
struct device_node *gpio_node;
// 在当前设备节点下查找"gpio"子节点
gpio_node = of_find_node_by_name(np, "gpio");
if (!gpio_node) {
dev_err(&pdev->dev, "Failed to find gpio child node\n");
return -ENODEV;
}
// 从gpio节点获取GPIO编号
int gpio = of_get_named_gpio_flags(gpio_node, "gpios", 0, NULL);
of_node_put(gpio_node);
if (gpio_is_valid(gpio)) {
// 配置并使用GPIO
gpio_request(gpio, "driver-gpio");
gpio_direction_output(gpio, 1);
}
return 0;
}
4.5 性能优化场景:结合of_node_get与缓存
// 缓存常用节点以避免重复查找
static struct device_node *cached_eth_node;
struct device_node *get_eth_node(void) {
if (!cached_eth_node) {
cached_eth_node = of_find_node_by_name(NULL, "ethernet");
if (cached_eth_node)
of_node_get(cached_eth_node); // 增加持久引用
}
return cached_eth_node;
}
// 驱动卸载时释放缓存
void release_eth_node(void) {
if (cached_eth_node) {
of_node_put(cached_eth_node);
cached_eth_node = NULL;
}
}
5. 性能优化方案
5.1 查找性能优化对比表
| 优化方案 | 实现方式 | 时间复杂度 | 适用场景 | 实现难度 |
|---|---|---|---|---|
| 节点缓存 | 保存已查找节点指针 | O(1) | 重复查找同一节点 | ★☆☆☆☆ |
| 别名机制 | 使用aliases节点映射 | O(1) | 固定硬件路径 | ★★☆☆☆ |
| 设备树overlay | 预定义节点路径 | O(1) | 运行时动态设备 | ★★★☆☆ |
| 哈希表索引 | 构建节点名称哈希表 | O(1) | 大规模设备树 | ★★★★☆ |
| 编译时解析 | DTC生成静态查找表 | O(1) | 嵌入式固定硬件 | ★★★★★ |
5.2 推荐优化:利用设备树别名机制
设备树片段:
aliases {
eeprom0 = &eeprom;
gpio0 = &gpio_controller;
};
i2c@12340000 {
eeprom: eeprom@50 {
name = "eeprom";
reg = <0x50>;
};
};
内核代码:
// 通过别名直接查找,避免遍历
struct device_node *eeprom_node = of_find_node_by_alias(NULL, "eeprom0");
5.3 高级优化:自定义哈希索引实现
// 定义节点名称哈希表
struct hlist_head node_hash[256]; // 256个桶的哈希表
// 初始化哈希表(驱动加载时执行)
void init_node_hash(void) {
struct device_node *np;
int i;
// 初始化哈希表
for (i = 0; i < 256; i++)
INIT_HLIST_HEAD(&node_hash[i]);
// 遍历所有节点并加入哈希表
for_each_of_allnodes(np) {
unsigned int hash = jhash(np->name, strlen(np->name), 0) % 256;
hlist_add_head(&np->hash_node, &node_hash[hash]);
}
}
// 哈希查找函数
struct device_node *hash_find_node_by_name(const char *name) {
struct device_node *np;
unsigned int hash = jhash(name, strlen(name), 0) % 256;
hlist_for_each_entry(np, &node_hash[hash], hash_node) {
if (!strcmp(np->name, name))
return of_node_get(np);
}
return NULL;
}
6. 常见错误案例与调试方法
6.1 错误案例1:忘记释放节点引用
// 错误示例:未调用of_node_put导致内存泄漏
struct device_node *node = of_find_node_by_name(NULL, "led");
if (node) {
// 使用节点...但未释放引用
}
// 正确示例
struct device_node *node = of_find_node_by_name(NULL, "led");
if (node) {
// 使用节点
of_node_put(node); // 必须释放
}
6.2 错误案例2:混淆name与full_name
设备树节点:
gpio@12340000 { // full_name是"gpio@12340000"
name = "gpio"; // name属性是"gpio"
};
错误代码:
// 错误:使用full_name作为查找条件
struct device_node *node = of_find_node_by_name(NULL, "gpio@12340000");
正确代码:
// 正确:使用name属性值查找
struct device_node *node = of_find_node_by_name(NULL, "gpio");
6.3 调试工具:节点查找跟踪
# 启用设备树调试
echo 1 > /sys/module/of_core/parameters/debug
# 查看内核日志中的查找过程
dmesg | grep "of_find_node_by_name"
7. 内核实现流程图
8. 总结与最佳实践
8.1 核心要点总结
- 引用管理:每次成功获取节点后必须调用
of_node_put释放 - 查找起点:非NULL的
from参数可大幅提升查找效率 - 名称匹配:严格匹配
name属性,而非full_name中的单元地址 - 性能优化:小规模使用缓存,大规模使用别名或哈希索引
- 错误处理:始终检查返回值,避免NULL指针解引用
8.2 最佳实践清单
- 优先使用
of_get_child_by_name替代从根节点查找子节点 - 驱动中缓存常用节点引用,避免重复查找
- 对同名多节点场景,使用
full_name区分不同实例 - 设备树设计时为关键节点定义别名
- 禁止在中断上下文使用该函数(可能导致长时间阻塞)
8.3 扩展学习路径
of_find_node_by_type:按device_type属性查找of_find_compatible_node:按compatible属性查找of_get_child_by_name:直接获取指定父节点的子节点of_parse_phandle:解析phandle属性引用的节点
【免费下载链接】linux Linux kernel source tree 项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



