转自:https://blog.csdn.net/av_geek/article/details/49640433

IDR机制在Linux内核中指的是整数ID管理机制。

实质上来讲,这就是一种将一个整数ID号和一个指针关联在一起的机制。

这个机制最早在03年2月加入内核,当时作为POSIX定时器的一个补丁。现在,内核中很多地方都可以找到它的身影。

IDR机制原理:

IDR机制适用在那些需要把某个整数和特定指针关联在一起的地方。例如,在IIC总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送设备的地址。当适配器要访问总线上的IIC设备时,首先要知道它们的ID号,同时要在内核中建立一个用于描述该设备的结构体,和驱动程序

将ID号和设备结构体结合起来,如果使用数组进行索引,一旦ID 号很大,则用数组索引会占据大量内存空间。这显然不可能。或者用链表,但是,如果总线中实际存在的设备很多,则链表的查询效率会很低。

此时,IDR机制应运而生。该机制内部采用红黑树实现,可以很方便的将整数和指针关联起来,并且具有很高的搜索效率

struct idr {
    struct idr_layer *top;
    struct idr_layer *id_free;
    int          layers; /* only valid without concurrent changes */
    int          id_free_cnt;
    spinlock_t      lock;
};

struct idr_layer {
    unsigned long         bitmap; /* A zero bit means "space here" */
    struct idr_layer    *ary[1<<IDR_BITS];
    int             count;     /* When zero, we can release it */
    int             layer;     /* distance from leaf */
    struct rcu_head         rcu_head;
};

宏定义并且初始化一个名为name的IDR:

#define DEFINE_IDR(name)    struct idr name = IDR_INIT(name)

#define IDR_INIT(name)                        \
{                                \
    .top        = NULL,                    \
    .id_free    = NULL,                    \
    .layers     = 0,                    \
    .id_free_cnt    = 0,                    \
    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),    \
}

动态初始化IDR:

void idr_init(struct idr *idp)
{
    memset(idp, 0, sizeof(struct idr));
    spin_lock_init(&idp->lock);
}

分配存放ID号的内存:

每次通过IDR获得ID号之前 ,需要为ID号先分配内存。分配内存的函数是idr_pre_get().成功返回1,失败放回0

第一个参数是指向IDR的指针,第二个参数是内存分配标志。

int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
    while (idp->id_free_cnt < IDR_FREE_MAX) {
        struct idr_layer *new;
        new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);
        if (new == NULL)
            return (0);
        move_to_free_list(idp, new);
    }
    return 1;
}

它调用了 :

static void move_to_free_list(struct idr *idp, struct idr_layer *p)
{
    unsigned long flags;

/*
     * Depends on the return element being zeroed.
     */
    spin_lock_irqsave(&idp->lock, flags);
    __move_to_free_list(idp, p);
    spin_unlock_irqrestore(&idp->lock, flags);
}

static void __move_to_free_list(struct idr *idp, struct idr_layer *p)
{
    p->ary[0] = idp->id_free;
    idp->id_free = p;
    idp->id_free_cnt++;
}

分配ID号并将ID号和指针关联:

参数idp是之前,通过idr_init()初始化的idr指针,或者DEFINE_IDR宏定义的指针。参数ptr是和ID号相关联 的 指针。参数id由内核自动分配的ID号。参数start_id是起始ID号。

成功返回0,失败返回负值:

int idr_get_new(struct idr *idp, void *ptr, int *id)
{
    int rv;

rv = idr_get_new_above_int(idp, ptr, 0);
    /*
     * This is a cheap hack until the IDR code can be fixed to
     * return proper error values.
     */
    if (rv < 0)
        return _idr_rc_to_errno(rv);
    *id = rv;
    return 0;
}

int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id)
{
    int rv;

rv = idr_get_new_above_int(idp, ptr, starting_id);
    /*
     * This is a cheap hack until the IDR code can be fixed to
     * return proper error values.
     */
    if (rv < 0)
        return _idr_rc_to_errno(rv);
    *id = rv;
    return 0;
}

这两个函数唯一的区别是起始ID号不同:

它们都调用了:

static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{
    struct idr_layer *pa[MAX_LEVEL];
    int id;

id = idr_get_empty_slot(idp, starting_id, pa);
    if (id >= 0) {
        /*
         * Successfully found an empty slot.  Install the user
         * pointer and mark the slot full.
         */
        rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],
                (struct idr_layer *)ptr);
        pa[0]->count++;
        idr_mark_full(pa, id);
    }

return id;
}

它调用了:

static int idr_get_empty_slot(struct idr *idp, int starting_id,
                  struct idr_layer **pa)
{
    struct idr_layer *p, *new;
    int layers, v, id;
    unsigned long flags;

id = starting_id;
build_up:
    p = idp->top;
    layers = idp->layers;
    if (unlikely(!p)) {
        if (!(p = get_from_free_list(idp)))
            return -1;
        p->layer = 0;
        layers = 1;
    }
    /*
     * Add a new layer to the top of the tree if the requested
     * id is larger than the currently allocated space.
     */
    while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {
        layers++;
        if (!p->count) {
            /* special case: if the tree is currently empty,
             * then we grow the tree by moving the top node
             * upwards.
             */
            p->layer++;
            continue;
        }
        if (!(new = get_from_free_list(idp))) {
            /*
             * The allocation failed.  If we built part of
             * the structure tear it down.
             */
            spin_lock_irqsave(&idp->lock, flags);
            for (new = p; p && p != idp->top; new = p) {
                p = p->ary[0];
                new->ary[0] = NULL;
                new->bitmap = new->count = 0;
                __move_to_free_list(idp, new);
            }
            spin_unlock_irqrestore(&idp->lock, flags);
            return -1;
        }
        new->ary[0] = p;
        new->count = 1;
        new->layer = layers-1;
        if (p->bitmap == IDR_FULL)
            __set_bit(0, &new->bitmap);
        p = new;
    }
    rcu_assign_pointer(idp->top, p);
    idp->layers = layers;
    v = sub_alloc(idp, &id, pa);
    if (v == IDR_NEED_TO_GROW)
        goto build_up;
    return(v);
}
和

static void idr_mark_full(struct idr_layer **pa, int id)
{
    struct idr_layer *p = pa[0];
    int l = 0;

__set_bit(id & IDR_MASK, &p->bitmap);
    /*
     * If this layer is full mark the bit in the layer above to
     * show that this part of the radix tree is full.  This may
     * complete the layer above and require walking up the radix
     * tree.
     */
    while (p->bitmap == IDR_FULL) {
        if (!(p = pa[++l]))
            break;
        id = id >> IDR_BITS;
        __set_bit((id & IDR_MASK), &p->bitmap);
    }
}

idr_get_new还调用了:

#define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC)                             //这是一个错误处理的宏

通过ID号查找对应的指针:

void *idr_find(struct idr *idp, int id)
{
    int n;
    struct idr_layer *p;

p = rcu_dereference(idp->top);
    if (!p)
        return NULL;
    n = (p->layer+1) * IDR_BITS;

/* Mask off upper bits we don't use for the search. */
    id &= MAX_ID_MASK;

if (id >= (1 << n))
        return NULL;
    BUG_ON(n == 0);

while (n > 0 && p) {
        n -= IDR_BITS;
        BUG_ON(n != p->layer*IDR_BITS);
        p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
    }
    return((void *)p);
}

删除ID号:

void idr_remove(struct idr *idp, int id)
{
    struct idr_layer *p;
    struct idr_layer *to_free;

/* Mask off upper bits we don't use for the search. */
    id &= MAX_ID_MASK;

sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);
    if (idp->top && idp->top->count == 1 && (idp->layers > 1) &&
        idp->top->ary[0]) {
        /*
         * Single child at leftmost slot: we can shrink the tree.
         * This level is not needed anymore since when layers are
         * inserted, they are inserted at the top of the existing
         * tree.
         */
        to_free = idp->top;
        p = idp->top->ary[0];
        rcu_assign_pointer(idp->top, p);
        --idp->layers;
        to_free->bitmap = to_free->count = 0;
        free_layer(to_free);
    }
    while (idp->id_free_cnt >= IDR_FREE_MAX) {
        p = get_from_free_list(idp);
        /*
         * Note: we don't call the rcu callback here, since the only
         * layers that fall into the freelist are those that have been
         * preallocated.
         */
        kmem_cache_free(idr_layer_cache, p);
    }
    return;
}

它调用了:

static void sub_remove(struct idr *idp, int shift, int id)
{
    struct idr_layer *p = idp->top;
    struct idr_layer **pa[MAX_LEVEL];
    struct idr_layer ***paa = &pa[0];
    struct idr_layer *to_free;
    int n;

*paa = NULL;
    *++paa = &idp->top;

while ((shift > 0) && p) {
        n = (id >> shift) & IDR_MASK;
        __clear_bit(n, &p->bitmap);
        *++paa = &p->ary[n];
        p = p->ary[n];
        shift -= IDR_BITS;
    }
    n = id & IDR_MASK;
    if (likely(p != NULL && test_bit(n, &p->bitmap))){
        __clear_bit(n, &p->bitmap);
        rcu_assign_pointer(p->ary[n], NULL);
        to_free = NULL;
        while(*paa && ! --((**paa)->count)){
            if (to_free)
                free_layer(to_free);
            to_free = **paa;
            **paa-- = NULL;
        }
        if (!*paa)
            idp->layers = 0;
        if (to_free)
            free_layer(to_free);
    } else
        idr_remove_warning(id);
}

和

static struct idr_layer *get_from_free_list(struct idr *idp)
{
    struct idr_layer *p;
    unsigned long flags;

spin_lock_irqsave(&idp->lock, flags);
    if ((p = idp->id_free)) {
        idp->id_free = p->ary[0];
        idp->id_free_cnt--;
        p->ary[0] = NULL;
    }
    spin_unlock_irqrestore(&idp->lock, flags);
    return(p);
}

Linux IDR机制【转】的更多相关文章

  1. 浅析linux内核中的idr机制

    idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制.这个机制最早是在2003年2月加入内核的,当时是作为POSIX定时器的一个补丁.现在, ...

  2. (linux)idr(integer ID management)机制

     最近研究进程间通信,遇到了idr相关的函数,为了扫清障碍,先研究了linux的idr机制. IDR(integer ID management)的要完成的任务是给要管理的对象分配一个唯一的ID,于 ...

  3. linux内核 idr机制

    idr机制解决了什么问题?为什么需要idr机制(或者说,idr机制这种解决方案,相对已有的其他方案,有什么优势所在) ? idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整 ...

  4. Linux模块机制浅析

    Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...

  5. android & Linux uevent机制

    Linux uevent机制 Uevent是内核通知android有状态变化的一种方法,比如USB线插入.拔出,电池电量变化等等.其本质是内核发送(可以通过socket)一个字符串,应用层(andro ...

  6. 利用linux信号机制调试段错误(Segment fault)

    在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止.如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过 ...

  7. Linux 内存机制详解宝典

    Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...

  8. Linux Namespaces机制——实现

    转自:http://www.cnblogs.com/lisperl/archive/2012/05/03/2480573.html 由于Linux内核提供了PID,IPC,NS等多个Namespace ...

  9. Linux Namespaces机制

    转自:http://www.cnblogs.com/lisperl/archive/2012/05/03/2480316.html Linux Namespaces机制提供一种资源隔离方案.PID,I ...

随机推荐

  1. ADC触摸屏

    目录 ADC触摸屏 硬件原理 等效电路 测量逻辑 程序设计(一)获得ADC 寄存器初始化 中断初始化 ADC模式(中断.测量) 中断函数 程序设计(二)获得坐标 生产者与消费者 ADC获取 程序优化 ...

  2. docker 基础之操作容器

    Docker子命令分类 Docker 环境信息 info .version 容器生命周期管理 Create.exec.kill.pause.restart.rm.run.start.stop.unpa ...

  3. jms和activemq

    一.什么是JMS JMS是java message service的缩写即java消息服务,是java定义的消息中间件(MOM)的技术规范(类似玉JDBC).用于程序之间的异步通信,如果两个应用程序需 ...

  4. Docker: 安装配置入门[二]

    一.安装配置启动 1.环境 [root@docker1 ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) [root@d ...

  5. vue @blur v-model数据没有更新问题

    今天遇到一个问题,是一个输入框绑定了一个失去焦点事件,要发送一个客户填写的数据给后台查询然后拿到返回值把它渲染到页面上,但是从后台获取到的数据却没有在页面上渲染出来,console.log打印时显示数 ...

  6. angular,vue,react的父子通信

    父子通信 父传子 vue: 父组件:<child :msg="datamsg" ></child> //子组件的msg属性上加数据,datamsg是数据 子 ...

  7. Linux文件权限设置

    基本概念 https://linux.cn/article-7418-1.html#3_8880 用户管理 文件权限设置 -添加用户账户08% -理解 /etc/passwd 中的内容12% -理解 ...

  8. hasnMap的基本操作 源码(三)

    一.初始化: hashMap有四种初始化方式: public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity ...

  9. 使用PHP+MySql+Ajax+jQuery实现省市区三级联动功能

    使用PHP+MySql+Ajax+jQuery实现省市区三级联动功能 要求:写一个省市区(或者年月日)的三级联动,实现地区或时间的下拉选择. 实现技术:php ajax 实现:省级下拉变化时市下拉区下 ...

  10. Python之线程 2 - Python实现线程

    一 python与线程 1.全局解释器锁GIL(用一下threading模块之后再来看~~) 2.python线程模块的选择 二 Threading模块 1.线程创建 2.多线程与多进程 3.多线程实 ...