最近研究进程间通信,遇到了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; //当前的层数。
layers; //当前的层数。
int
id_free_cnt;// 预备队的个数。
id_free_cnt;// 预备队的个数。
spinlock_t
lock;
lock;
};
struct idr_layer {
unsigned long
bitmap; /* A zero bit means "space here" */
bitmap; /* A zero bit means "space here" */
struct idr_layer
*ary[1<<IDR_BITS];
*ary[1<<IDR_BITS];
int
count;
/* When zero, we can release it */
count;
/* 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);
}