• 浅析linux内核中的idr机制


    idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制。这个机制最早是在2003年2月加入内核的,当时是作为POSIX定时器的一个补丁。现在,在内核的很多地方都可以找到idr的身影。
    idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。举个例子,在I2C总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。如果我们的PC是一个I2C总线上的主节点,那么要访问总线上的其他设备,首先要知道他们的ID号,同时要在pc的驱动程序中建立一个用于描述该设备的结构体。
    此时,问题来了,我们怎么才能将这个设备的ID号和他的设备结构体联系起来呢?最简单的方法当然是通过数组进行索引,但如果ID号的范围很大(比如32位的ID号),则用数组索引显然不可能;第二种方法是用链表,但如果网络中实际存在的设备较多,则链表的查询效率会很低。遇到这种清况,我们就可以采用idr机制,该机制内部采用radix树实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。http://hovertree.com/menu/linux/
    (1)获得idr
    要在代码中使用idr,首先要包括<linux/idr.h>。接下来,我们要在代码中分配idr结构体,并初始化:
        void idr_init(struct idr *idp);
    其中idr定义如下:
    struct idr {
            struct idr_layer *top;
            struct idr_layer *id_free;
            int               layers;
            int               id_free_cnt;
            spinlock_t        lock;
    };
    /* idr是idr机制的核心结构体  何问起 hovertree.com */
    (2)为idr分配内存
    int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
    每次通过idr获得ID号之前,需要先分配内存。
    返回0表示错误,非零值代表正常
    (3)分配ID号并将ID号和指针关联
    int idr_get_new(struct idr *idp, void *ptr, int *id);
    int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);
    idp: 之前通过idr_init初始化的idr指针
    id:  由内核自动分配的ID号
    ptr: 和ID号相关联的指针
    start_id: 起始ID号。内核在分配ID号时,会从start_id开始。如果为I2C节点分配ID号,可以将设备地址作为start_id
    函数调用正常返回0,如果没有ID可以分配,则返回-ENOSPC
    在实际中,上述函数常常采用如下方式使用:
    again:
      if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) {
        /* No memory, give up entirely */
      }
      spin_lock(&my_lock);
      result = idr_get_new(&my_idr, &target, &id);
      if (result == -EAGAIN) {
        sigh();
        spin_unlock(&my_lock);
        goto again;
      }/* 何问起 hovertree.com */
    (4)通过ID号搜索对应的指针
    void *idr_find(struct idr *idp, int id);
    返回值是和给定id相关联的指针,如果没有,则返回NULL
    (5)删除ID
    要删除一个ID,使用:
    void idr_remove(struct idr *idp, int id);
    通过上面这些方法,内核代码可以为子设备,inode生成对应的ID号。这些函数都定义在<linux-2.6.xx/lib/idr.c>中

    下面,我们通过分析I2C协议的核心代码,来看一看idr机制的实际应用:
    <linux-2.6.23/drivers/i2c/i2c-core.c>
    ...
    <linux/idr.h>   /* idr头文件 */
    ...
    static DEFINE_IDR(i2c_adapter_idr); /* 声明idr */
    ...
    /* 
      采用动态总线号声明并注册一个i2c适配器(adapter),可睡眠
      针对总线号可动态指定的设备,如基于USB的i2c设备或pci卡
     */
    int i2c_add_adapter(struct i2c_adapter *adapter)
    {
            int     id, res = 0;
    retry:
            if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
                    return -ENOMEM;
            mutex_lock(&core_lists);
            /* __i2c_first_dynamic_bus_num是当前系统允许的动态总线号的最大值 */
            res = idr_get_new_above(&i2c_adapter_idr, adapter,                 __i2c_first_dynamic_bus_num, &id);
            mutex_unlock(&core_lists);
            if (res < 0) {
                    if (res == -EAGAIN)
                            goto retry;
                    return res;
            }
            adapter->nr = id;
            return i2c_register_adapter(adapter);
    }
    EXPORT_SYMBOL(i2c_add_adapter);

    /* 
      采用静态总线号声明并注册一个i2c适配器(adapter)
     */
    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
            int     id;
            int     status;
            if (adap->nr & ~MAX_ID_MASK)
                    return -EINVAL;
    retry:
            if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
                    return -ENOMEM;
            mutex_lock(&core_lists);
            /* "above" here means "above or equal to", sigh;
             * we need the "equal to" result to force the result
             */
            status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
            if (status == 0 && id != adap->nr) {
                    status = -EBUSY;
                    idr_remove(&i2c_adapter_idr, id);
            }
            mutex_unlock(&core_lists);
            if (status == -EAGAIN)
                    goto retry;
            if (status == 0)
                    status = i2c_register_adapter(adap);
            return status;
    }
    EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);

    /* 注销一个i2c适配器 */
    int i2c_del_adapter(struct i2c_adapter *adap)
    {
      ...
      /* free bus id */
      idr_remove(&i2c_adapter_idr, adap->nr);
      ...
      return res;
    }
    EXPORT_SYMBOL(i2c_del_adapter);

    /* 通过ID号获得i2c_adapter设备结构体 */
    struct i2c_adapter* i2c_get_adapter(int id)
    {
            struct i2c_adapter *adapter;
            mutex_lock(&core_lists);
            adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
            if (adapter && !try_module_get(adapter->owner))
                    adapter = NULL;
            mutex_unlock(&core_lists);
            return adapter;
    }
    EXPORT_SYMBOL(i2c_get_adapter);
     
  • 相关阅读:
    C++ 什么是多态
    *和&的使用
    静态链接库与动态链接库
    利尔达CC3200模块烧写程序笔记
    创龙TMS320C6748开发找不到 tl.dsp.evm6748的问题研究
    RTSC和XDCTool的理解
    创龙DSP6748开发板SYS/BIOS的LED闪烁-第2篇
    Coap协议学习笔记-第一篇
    linux进程的学习笔记(未完)
    创龙DSP6748开发板LED闪烁-第一篇
  • 原文地址:https://www.cnblogs.com/roucheng/p/linuxidr.html
Copyright © 2020-2023  润新知