idr机制--integer ID management(一)

来源:互联网 发布:机顶盒点播软件下载 编辑:程序博客网 时间:2024/05/18 01:24
最近研究进程间通信,遇到了idr相关的函数,为了扫清障碍,先研究了linux的idr机制。

        所谓IDR,其实就是和身份证的含义差不多,我们知道,每个人有一个身份证,身份证只是 一串数字,从数字,我们就能知道这个人的信息。同样道理,idr的要完成的任务是给要管理的对象分配一个数字,可以通过这个数字找到要管理的对象。

 ID -------------------->struct obj* 


应用IDR机制时要包含头文件<linux/idr.h>。

struct idr {
struct idr_layer *top;  //idr的top层,可以方便的理解为根节点。
struct idr_layer *id_free; //id_free为首的形成一个链表,这个是预备队,
                                           //并没有参与到top为根的节点中去
int layers; //当前的层数。
int id_free_cnt;// 预备队的个数。
spinlock_t lock;
};


struct idr_layer {
unsigned longbitmap; /* A zero bit means "space here" */
struct idr_layer*ary[1<<IDR_BITS];
intcount; /* When zero, we can release it */
};

      IDR_BITS 在32位操作系统是5 ,64位操作系统是6,我们以32位操作系统为例。

      本文的介绍以两层的为例。layers = 2.如下图所示

      idr中的top指向的是当前正在工作的最高层的idr_layer,即图中的A,top的ary是个指针数组,指向
低一层的idr_layer。top层ary指针数组不一定都指向已经分配了的低一层idr_layer。也可能某个指针指
向NULL。如下图的ary[1]就指向NULL。

       最后一层idr_layer 叶子层  例如B,他的指针数组ary中的元素,如果分配出去了那么指向某个结构体的地址,这个地址指向要管理的数据结构。如果没有分配出去,指针指向NULL。对于叶子层而言,判断指针数组某个元素是否指向有意义的数据结构,用位图bitmap。bitmap对应的位 是1,表示ary数组的对应元素指向某有意义的数据结构。

       最后一层的bitmap的含义已经介绍,但是top层(或者层数大于2的时候,中间某层)bitmap的含义是什么呢?以两层为例,如果图中B的bitmap是0xFFFFFFFF,即每一个指针都分配出去了,那么A的bitmap的第0位置1.同样如果A的bitmap的第2位是1,表示ary[2]指向的C的bitmap是0xFFFFFFFF,即C也ary数组也分配完毕。

      这部分是函数idr_mark_full来实现。

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);// 叶子层数字id对应的位 置1.
/*
* 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]))              // pa[++l]记录的上一层idr_layer。
break;
id = id >> IDR_BITS;
__set_bit((id & IDR_MASK), &p->bitmap); //如果由于本层满了,则上一层对应位置1.
                                                                           //循环检测。
}
}


      介绍完负责工作的部分,下面介绍预备役。所谓预备役就是id_free指向的空闲的idr_layer。所谓空闲是指,这些idr_layer并没有投入。如果需要分配一个idr_layer,首先将id_free指向的idr_layer取出来使用,同时id_free指向下一个。即如下图所示,如果需要分配,D被取出来使用,同时id_free指针指向E,同时id_freecnt减一。
     将预备役投入使用是函数alloc_layer完成的。
      
static struct idr_layer *alloc_layer(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]; // id_free 指向D的下一位 E 
idp->id_free_cnt--;         // 预备役的个数减1
p->ary[0] = NULL;          //D要被使用了,第0个指针不再指向E,初始化为NULL
}
spin_unlock_irqrestore(&idp->lock, flags);
return(p);  // 返回D
}


     有个问题是预备役是怎么来的,如果预备役分配光了怎么办。分配光了也没有关系,还好我们有idr_pre_get函数。

#if BITS_PER_LONG == 32
# define IDR_BITS 5

 #define MAX_ID_SHIFT (sizeof(int)*8 - 1)               //31
#define MAX_LEVEL         (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS              //7
#define IDR_FREE_MAX      MAX_LEVEL + MAX_LEVEL                                 //14

      坦白说,MAX_LEVEL的含义是什么,我并不清楚。为什么一次分配14个idr_layer充当预备役我并不清楚。请清楚的兄弟不吝赐教。
      这个函数的含义就是我要分配14个idr_layer,充当预备役。如果中间分配失败,那么能分配几个算几个。投入预备役的函数是free_layer。比较好懂我就不解释了。
     
   
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_alloc(idr_layer_cache, gfp_mask);
if (new == NULL)
return (0);
free_layer(idp, new);
}
return 1;
}

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

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

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

    从预备役机制上看,我们可以得到使用idr编程流程应该是这样的。
    首先调用idr_pre_get,来分配可用的idr_layer,投入预备役,接下来调用idr_get_new,
给要管理的对象target分配一个数字id,这个过程中可能会调用alloc_layer,将预备役中的
idr_layer投入使用,用在top为根管理结构中。  终有一天,预备役也被打光了idr_get_new
函数返回-EAGAIN,告诉我们,预备役全部阵亡,于是,我们从-EAGAIN的遗言中,知道,我们需要调用
idr_pre_get来充实预备役了。         

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;
       }

终于终于无限接近我们的真正想干的事情了。下面我们介绍如何将要管理的数据结构和某个ID组合起来。
太晚了,下一篇博文介绍。