Linux内核设备树节点查找实战:of_find_node_by_name全解析

Linux内核设备树节点查找实战:of_find_node_by_name全解析

【免费下载链接】linux Linux kernel source tree 【免费下载链接】linux 项目地址: 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"
  • 链表结构:通过childsibling形成树状层次结构

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. 内核实现流程图

mermaid

8. 总结与最佳实践

8.1 核心要点总结

  1. 引用管理:每次成功获取节点后必须调用of_node_put释放
  2. 查找起点:非NULL的from参数可大幅提升查找效率
  3. 名称匹配:严格匹配name属性,而非full_name中的单元地址
  4. 性能优化:小规模使用缓存,大规模使用别名或哈希索引
  5. 错误处理:始终检查返回值,避免NULL指针解引用

8.2 最佳实践清单

  • 优先使用of_get_child_by_name替代从根节点查找子节点
  • 驱动中缓存常用节点引用,避免重复查找
  • 对同名多节点场景,使用full_name区分不同实例
  • 设备树设计时为关键节点定义别名
  • 禁止在中断上下文使用该函数(可能导致长时间阻塞)

8.3 扩展学习路径

  1. of_find_node_by_type:按device_type属性查找
  2. of_find_compatible_node:按compatible属性查找
  3. of_get_child_by_name:直接获取指定父节点的子节点
  4. of_parse_phandle:解析phandle属性引用的节点

【免费下载链接】linux Linux kernel source tree 【免费下载链接】linux 项目地址: https://gitcode.com/GitHub_Trending/li/linux

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值