转自: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. servlet模拟SpringMVC

    1. web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=&q ...

  2. MyBatis-DynamicSQL IF判断

    在写动态sql时发现字符串的判断没有生效 <if test="CLLX != null and CLLX != ''"> and a.CLLX = #{CLLX} &l ...

  3. 设计模式---接口隔离模式之中介者模式(Mediator)

    一:概念 在Mediator模式中,类之间的交互行为被统一放在Mediator的对象中,对象通过Mediator对象同其他对象交互.Mediator对象起到控制器的作用 二:动机 在软件构建的过程中, ...

  4. C#设计模式(9)——代理模式

    1.代理模式介绍 在软件开发中有时会遇到不能直接使用对象的问题,如我们要使用的对象在进程外,甚至在远程的机器上,但是我们要使用这个对象的功能怎么办呢?代理模式就可以用来解决这个问题.举一个生活中的例子 ...

  5. jQuery使用(一):jQuery对象与选择器

    一.简单的一些介绍 1.jQuery是由普通的是由一些系列操作DOM节点的函数和一些其他的工具方法组成的js库. 2.为什么要使用jQuery库? jQuery面向用户良好的设计在使用过程中彻底解放了 ...

  6. java代码实现ftp服务器的文件上传和下载

    java代码实现文件上传到ftp服务器: 1:ftp服务器安装: 2:ftp服务器的配置: 启动成功: 2:客户端:代码实现文件的上传与下载: 1:依赖jar包: 2:sftpTools   工具类: ...

  7. 细说REST API

    1. REST概述 REST(英文:Representational State Transfer,又称具象状态传输)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一 ...

  8. SQL Server进阶(六)表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数

    概述 表表达式是一种命名的查询表达式,代表一个有效地关系表.可以像其他表一样,在数据处理中使用表表达式. SQL Server支持四种类型的表表达式:派生表,公用表表达式,视图和内联表值函数. 为什么 ...

  9. mysql查询表达式解析

    1.mysql> SHOW COLUMNS FROM users;+----------+----------------------+------+-----+---------+------ ...

  10. Flume配置Replicating Channel Selector

    1 官网内容 上面的配置是r1获取到的内容会同时复制到c1 c2 c3 三个channel里面 2 详细配置信息 # Name the components on this agent a1.sour ...