下面分析如果给一个obj分配个ID。
提供两个函数给obj分配ID
int idr_get_new(struct idr *idp, void *ptr, int *id)
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id)
参数说明:idp---不说了,管理结构idr的指针,对应示意图中最左面的那个结构。
ptr---指向要管理的结构的指针,我们的任务就是给它分配个小数字,作为他的身份证。成功之后,
我们可以拿着这个ID,直接找到ptr。
id----输出参数,将分配的数字存入id。
这两个函数其中idr_get_new比较乖,比较好说话,随便给他分配一个没人用的id就可以,他他不挑不捡。第二个函数idr_get_new_above有点难说话,要求挺多,他有个参数starting_id,要求分配不小于starting_id的一个数字作为id。
两个函数都是调用了idr_get_new_above_int,区别是idr_get_new将starting_id填成了0.表示随便给分配个大于0的没被别人用的id就行。
-EAGAIN的意思上面一讲提到过,这个是预备役全体阵亡的遗言,没有空闲的idr_layer用来分配了,所以失败了,如果用户非常需要给ptr分配个id,那么请先分配点预备役,即调用idr_pre_get。
-ENOSPC的含义是你小子要的id太大了,超过了MAX_ID_BIT,即2^31,idr说,我是管理小数字的结构,拜托不要那这么大的数字骚扰我。
if ((id >= MAX_ID_BIT) || (id < 0))
return -3; // sub_alloc函数中的语句
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) {
if (rv == -1)
return -EAGAIN;
else /* Will be -3 */
return -ENOSPC;
}
*id = rv;
return 0;
}
---------------------------------------------------------------------------------------
酝酿了半天,可以聊聊idr_get_new_above_int这个了。
idr_get_empty_slot函数是分配个大于starting_id的数字作为ptr的ID。如果分配成功,id>=0,将叶子节点id对应的ary数组的元素赋值为 ptr。同时将叶子层的count++,表示又分配出去一个。将叶子层的位图bitmap对应槽位置1的工作是idr_mark_full完成。如果叶子层全满了,则通知叶子层的父亲对应槽位置1,依次传递。
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.
*/
pa[0]->ary[id & IDR_MASK] = (struct idr_layer *)ptr;
pa[0]->count++;
idr_mark_full(pa, id);
}
return id;
}
OK,到了idr_get_empty_slot。这个函数是干重活的函数。需要仔细研读代码。这个函数不举例子很难描述清楚,举例子又显得特别琐碎,很头疼。建议读者从0开始分配一直分配到32需要分层,就可以理解代码的含义。
先讲初始化:
#define IDR_INIT(name) \
{ \
.top = NULL, \
.id_free = NULL, \
.layers = 0, \
.id_free_cnt = 0, \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
}
top等于NULL 表示我的32叉树还没建立起来,id_free =NULL,id_free_cnt=0表示不好意思,我的预备役也为空,没法为您分配idr_layer。这是最初的状态,32叉树连个根都没有,整个idr处于一穷二白的状态。
p = idp->top;
layers = idp->layers;
if (unlikely(!p)) {
if (!(p = alloc_layer(idp)))
return -1;
layers = 1;
}
idr_get_empty_slot这个部分,表示如果idr的32叉树连个根都没有,我需要分配一个idr_layer来当根。如果alloc_layer失败,表示预备役空了,惨了,只能返回失败,告诉调用者,预备役没了,请填充预备役。一般是可以分配的。
这个循环体的含义是,用户这个搞得这个starting_id太大了,或者低的id分配出去了,只能给用户分配个大的id。如果这个id大于了当前层数所能管理的最高ID,我们需要加一层了。
以上面的示意图为例,我们当前有两层结构,最多能管理32*32=1K个,我们能分配的最大id就是1023,如果用户要求我们分配大于等于1500的id,那么我们目前的两层结构是无法满足需要的,所以我们需要加一层。首先将layer++,表示我们的32叉树升级了,多了一层,从预备役分配出一个idr_layer,让新分配的new当根。p指针指向根。
如果分配的id不够大,不需要分层,那么这个while就不执行了,直接跳到sub_alloc函数。
while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {
layers++;
if (!p->count)//这个地方是应对特殊情况,比如0~31都没有分配,第一层还没有,用户 //上来要分配32或46这样明显是两层才能完成的结构
continue;
if (!(new = alloc_layer(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;
__free_layer(idp, new);
}
spin_unlock_irqrestore(&idp->lock, flags);
return -1;
}
new->ary[0] = p;
new->count = 1;
if (p->bitmap == IDR_FULL)
__set_bit(0, &new->bitmap);
p = new;
}
idp->top = p;
idp->layers = layers;
v = sub_alloc(idp, &id, pa);
if (v == -2)
goto build_up;
------------------------------------------------------------------------------------------
sub_alloc函数。
还是以示意图为例讲述。我们是两层的结构,p是32叉树的根节点top
如果用户要分配大于等于66的id,66=2*32+2,首先找到了我们要找的66是位于top->ary[2],我们需要确认 根的ary[2]这个分支是否还能分配。如果p->ary[2]对应的idr_layer 所有的槽位都分配出去了,客满,新的顾客无法入住,我们就不必白费劲去ary[2]这个分支去分配了。判断的办法就是m = find_next_bit(&bm, IDR_SIZE, n);这个函数很可爱,就是说我要找大于2 的所有分支,寻找第一个没有客满的分支。通过top层或者中间层bitmap的含义,如果某个分支全部客满,则在对应bitmap位置1 ,表示,不要去这个分支找了,找也白找。
然后一层层往下找,知道找到叶子层,在叶子层查找大于等于2的id。
各种情况我就不分析了,大家可以自己尝试分配一下
1 从0开始,分配,累加到33,差不多就可以理解idr_get_new这种情况的分配流程
2 不按常理出牌,乱分配,假如我第一个就要分配 大于37的,第二次就要分配大于1500的,之类的,
在走一遍流程,就可以理解相关的代码。
while (1) {
/*
* We run around this while until we reach the leaf node...
*/
n = (id >> (IDR_BITS*l)) & IDR_MASK;
bm = ~p->bitmap;
m = find_next_bit(&bm, IDR_SIZE, n);
if (m == IDR_SIZE) {
/* no space available go back to previous layer. */
l++;
oid = id;
id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
/* if already at the top layer, we need to grow */
if (!(p = pa[l])) {
*starting_id = id;
return -2;
}
/* If we need to go up one layer, continue the
* loop; otherwise, restart from the top.
*/
sh = IDR_BITS * (l + 1);
if (oid >> sh == id >> sh)
continue;
else
goto restart;
}
if (m != n) {
sh = IDR_BITS*l;
id = ((id >> sh) ^ n ^ m) << sh;
}
if ((id >= MAX_ID_BIT) || (id < 0))
return -3;
if (l == 0)
break;
/*
* Create the layer below if it is missing.
*/
if (!p->ary[m]) {
if (!(new = alloc_layer(idp)))
return -1;
p->ary[m] = new;
p->count++;
}
pa[l--] = p;
p = p->ary[m];
}