转自: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. 剑指Offer_编程题_11

    题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示.   class Solution { public: int NumberOf1(int n) { int size = 3 ...

  2. NorFlash 学习

    目录 NorFlash 学习 NOR/NAND Flash的差别 引脚接口 命令 参数识别(CFI) 写数据 程序设计 程序优化 其他设计点 title: NorFlash 学习 tags: ARM ...

  3. nginx做rails项目web服务器缓存配置方法

    nginx作为Web服务器.或反向代理服务器都可以使用缓存 一.作为Web服务器 nginx可以通过 expires 指令来设置响应头的过期时间,实现浏览器缓存(Browser Caching),即浏 ...

  4. inux进程/线程调度策略与 进程优先级

    目的: 系统性的认识linux的调度策略(SCHED_OTHER.SCHED_FIFO.SCHED_RR): 实时调度?分时调度? 混搭系统(实时任务+分时任务),怎样调度. linux的调度策略 l ...

  5. jQuery使用(八):运动方法

    show().hide().toggle() 参数:null或(duration,easing,callblack) fadeIn().fadeout().fadeToggle().fadeTo() ...

  6. jQuery使用(七):事件绑定与取消,及自定事件的实现原理

    实例方法: on() one() off() trigger() hover() 一.绑定事件与jQuery事件委托 $(selector).eventType(fn); $(selector).on ...

  7. 059、安装配置flannel(2019-03-28 周四)

    参考https://www.cnblogs.com/CloudMan6/p/7424858.html   build flannel   flannel 没哟现成的执行文件可用,必须自己build,最 ...

  8. js 操作对象 记录

    js 对象记录一下: let obj_1 = { name : 'james', age : '22', sex: '1' } for ( i in obj_1 ) { console.log(i) ...

  9. MySQL的一些基本命令笔记(2)

    1.逻辑运算符的补充 between 的用法:(在....之间) select column1,column2,......columnN from 表名 where columnX between ...

  10. luogu 2569 股票交易 单调队列dp

    注意转移方程 分1.凭空买 2.不买不卖 3.在原来基础上买 4.在原来基础上卖 四种情况 head=1,tail=0;再判断一下head<=tail也可以 #include<bits/s ...