idr机制(32叉树)

来源:互联网 发布:算法新解pdf 编辑:程序博客网 时间:2024/05/16 06:32

一.结构体

1.idr结构体

struct idr {struct idr_layer __rcu *top;//idr_layer顶层,32叉树的根struct idr_layer *id_free;//指向idr_layer的空闲链表intlayers;//idr_layer的层数量intid_free_cnt;//idr_layer空闲链表中剩余的idr_layer个数spinlock_tlock;};

2.idr_layer结构体

struct idr_layer {unsigned longbitmap;//标记位图,标记使用情况struct idr_layer __rcu*ary[1<<IDR_BITS];//子idr_layer数组ary[32]intcount;//ary数组使用情况intlayer;//层号struct rcu_headrcu_head;};

在32位系统中IDR_BITS的取值为5

#if BITS_PER_LONG == 32# define IDR_BITS 5# define IDR_FULL 0xfffffffful# define TOP_LEVEL_FULL (IDR_FULL >> 30)#elif BITS_PER_LONG == 64# define IDR_BITS 6# define IDR_FULL 0xfffffffffffffffful# define TOP_LEVEL_FULL (IDR_FULL >> 62)#else# error "BITS_PER_LONG is not 32 or 64"#endif

 

二.idr的初始化

#define IDR_INIT(name)\{\.top= NULL,\.id_free= NULL,\.layers = 0,\.id_free_cnt= 0,\.lock= __SPIN_LOCK_UNLOCKED(name.lock),\}#define DEFINE_IDR(name)struct idr name = IDR_INIT(name)

定义一个idr结构体并赋值

三.分配id

1.idr_pre_get

int idr_pre_get(struct idr *idp, gfp_t gfp_mask){while (idp->id_free_cnt < IDR_FREE_MAX) {//IDR_FREE_MAX=14struct idr_layer *new;//定义新的idr_layer结构体指针new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);//分配*new内存空间if (new == NULL)return (0);move_to_free_list(idp, new);//-->move_to_free_list}return 1;}EXPORT_SYMBOL(idr_pre_get);

move_to_free_list

static void move_to_free_list(struct idr *idp, struct idr_layer *p){unsigned long flags;spin_lock_irqsave(&idp->lock, flags);__move_to_free_list(idp, p);//-->__move_to_free_listspin_unlock_irqrestore(&idp->lock, flags);}

__move_to_free_list

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

第一次循环结果

接着循环

再接着

一直这样下去直到循环结束(14次)

2.idr_get_new和idr_get_new_above

idr_get_new

int idr_get_new(struct idr *idp, void *ptr, int *id){int rv;rv = idr_get_new_above_int(idp, ptr, 0);if (rv < 0)return _idr_rc_to_errno(rv);*id = rv;return 0;}EXPORT_SYMBOL(idr_get_new);

idr_get_new_above

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);if (rv < 0)return _idr_rc_to_errno(rv);*id = rv;return 0;}EXPORT_SYMBOL(idr_get_new_above);

两个函数都会调用idr_get_new_above_int函数,差别在于starting_id不同

下面分情况讨论,先以id为0走个过场

idr的top简称为根top,free简称为根free均为idr_layer指针类型,分别指向使用中和空闲idr_layer链表头

static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id){struct idr_layer *pa[MAX_LEVEL];//MAX_LEVEL=7int id;id = idr_get_empty_slot(idp, starting_id, pa);//-->idr_get_empty_slotif (id >= 0) {rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr//pa[0]->ary[0]=ptr 也就是idr_layer14->ary[0]=ptrpa[0]->count++;//idr_layer14->count++idr_mark_full(pa, id);//设置其位图-->走完0过场的效果见图c}return id;}

idr_get_empty_slot

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;//按常规出牌吧,假设这个为0build_up:p = idp->top;//根top指向的idr_layer NULLlayers = idp->layers;//获取layers层数量(0)if (unlikely(!p)) {//第一次运行idp->top=NULL,所以if条件为真,执行if分支的结果参考 图Aif (!(p = get_from_free_list(idp)))//>>>1-->get_from_free_list 从根free中获取一个idr_layer14return -1;p->layer = 0;//指定idr_layer14的层号为0layers = 1;//layers层数量设为1}//layers<6 && id>=2^(layers*5) 看需不需要增加层数 见图Bwhile ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {layers++;if (!p->count) {p->layer++;continue;}if (!(new = get_from_free_list(idp))) {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);//根top指向idr_layer14idp->layers = layers;//设置更新idr->layers层数量//----------------------------------------------分割线----------------------------------------------//以上部分主要处理layer相关,以下部分主要处理id相关v = sub_alloc(idp, &id, pa);//>>>2-->sub_allocif (v == IDR_NEED_TO_GROW)//IDR_NEED_TO_GROW=-2需要扩大goto build_up;return(v);}

图A:

图B

>>>get_from_free_list 从idr空闲idr_layer链表中获取第一个idr_layer

static struct idr_layer *get_from_free_list(struct idr *idp){struct idr_layer *p;//定义一个idr_layer指针unsigned long flags;spin_lock_irqsave(&idp->lock, flags);if ((p = idp->id_free)) {//根free获取一个空闲idr_layeridp->id_free = p->ary[0];//idr空闲链表指针指向第二个idr_layeridp->id_free_cnt--;//idr的空闲idr_layer个数减1(14-1)p->ary[0] = NULL;//断开第一个idr_layer和第二个idr_layer的联系}spin_unlock_irqrestore(&idp->lock, flags);return(p);}

这里先穿插一下32进制的计算,上面图B中2^0,2^5,2^10,2^15,2^20,2^25可以(32=2^5)理解成32^0,32^1,32^2,32^3,32^3,32^4,32^5

那么用32进制表达一个十进制数id可以套用一下公式

a的值属于[0,31]

an的值如何获得id/(32^n)即可,等同于id/(2^5^n)等同于id/((1<<5)^n)

an-1的值如何获得id>>(5*(n-1))即可

>>>sub_alloc

static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa){int n, m, sh;struct idr_layer *p, *new;int l, id, oid;unsigned long bm;id = *starting_id; restart:p = idp->top;//根topl = idp->layers;//l=1pa[l--] = NULL;//p[1]=NULL;l=0while (1) {n = (id >> (IDR_BITS*l)) & IDR_MASK;//计算对应的n值,属于[0,31]bm = ~p->bitmap;//取反位图m = find_next_bit(&bm, IDR_SIZE, n);//>>>1 find_next_bit 位图中偏移量为n处查找'1'if (m == IDR_SIZE) {//位图满了l++;oid = id;id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;if (id >= 1 << (idp->layers * IDR_BITS)) {*starting_id = id;return IDR_NEED_TO_GROW;}p = pa[l];BUG_ON(!p);sh = IDR_BITS * (l + 1);if (oid >> sh == id >> sh)continue;elsegoto restart;}if (m != n) {//期望的n值被占用,但可找到可用的m值sh = IDR_BITS*l;id = ((id >> sh) ^ n ^ m) << sh;//>>>2 重新计算id值}if ((id >= MAX_ID_BIT) || (id < 0))return IDR_NOMORE_SPACE;if (l == 0)//l==0跳出while循环break;if (!p->ary[m]) {new = get_from_free_list(idp);if (!new)return -1;new->layer = l-1;rcu_assign_pointer(p->ary[m], new);p->count++;}pa[l--] = p;p = p->ary[m];}pa[l] = p;//pa[0]=p 也就是idr_layer14return id;}

>>>find_next_bit

#define find_next_bit(p,sz,off)_find_next_bit_le(p,sz,off)//>>_find_next_bit_le

该宏的意思是在p指向的(大小为sz的)位图表中的第off个位置开始找寻可用(为"1")的格子,找到返回该位

_find_next_bit_le是汇编代码实现的定义在/arch/arm/lib/findbit.S

ENTRY(_find_next_bit_le)teqr1, #0beq3bandsip, r2, #7beq1b@ If new byte, goto old routine ARM(ldrbr3, [r0, r2, lsr #3]) THUMB(lsrr3, r2, #3) THUMB(ldrbr3, [r0, r3])movsr3, r3, lsr ip@ shift off unused bitsbne.L_foundorrr2, r2, #7@ if zero, then no bits hereaddr2, r2, #1@ align bit pointerb2b@ loop for next bitENDPROC(_find_next_bit_le)

.L_found找到合适的跳转

.L_found:#if __LINUX_ARM_ARCH__ >= 5rsbr0, r3, #0andr3, r3, r0clzr3, r3rsbr3, r3, #31addr0, r2, r3#elsetstr3, #0x0faddeqr2, r2, #4movner3, r3, lsl #4tstr3, #0x30addeqr2, r2, #2movner3, r3, lsl #2tstr3, #0x40addeqr2, r2, #1movr0, r2#endifcmpr1, r0@ Clamp to maxbitmovlor0, r1movpc, lr

>>>id值的计算的补充说明

首先前面n的取值n = (id >> (IDR_BITS*l)) & IDR_MASK;

IDR_MASK的定义#define IDR_MASK ((1 << IDR_BITS)-1)也就是说IDR_MASK=31等于2进制的1,1111b

所以&IDR_MASK只是框定n值落在0~31之间,掩码作用

那么不出意外的话n = (id >> (IDR_BITS*l))

接着

sh = IDR_BITS*l;
id = ((id >> sh) ^ n ^ m) << sh;

带入表达式中

id=((id >> IDR_BITS*l) ^ (id >> (IDR_BITS*l)) ^ m) << IDR_BITS*l;

异或的操作是相同为1,不同为0,结合起来化简得

id = ((1 ^ m) << sh=m<<sh

图C

^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_

 已经借用id0走了过场,下面分析下其他情况

static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id){struct idr_layer *pa[MAX_LEVEL];//定义父idr_layer数组int id;id = idr_get_empty_slot(idp, starting_id, pa);//获取idif (id >= 0) {rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr);//pa[0]->ary[id]=ptrpa[0]->count++;//idr_layer->count++idr_mark_full(pa, id);//标记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;//获取根toplayers = idp->layers;//获取层数量 layers=1if (unlikely(!p)) {//FALSEif (!(p = get_from_free_list(idp)))return -1;p->layer = 0;layers = 1;}while ((layers < 6) && (id >= (1 << (layers*5)))) {//参考图B,如果id值超过或等于对应层所能容纳的最大数,则进入循环layers++;//增加层数量if (!p->count) {//0~31没使用,直接使用32就属于这种情况p->layer++;//由于32需要添加1层的,所以之前的层的层号需要+1continue; //层数量也需要加1}if (!(new = get_from_free_list(idp))) {//空闲链表中获取新的idr_layerspin_lock_irqsave(&idp->lock, flags);//分配失败,--空闲idr_layer链表缺货for (new = p; p && p != idp->top; new = p) {//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;//新的idr_layer->ary[0]指向旧的idr_layernew->count = 1;//新的idr_layer计数加1new->layer = layers-1;//设置新的idr_layer的层号if (p->bitmap == IDR_FULL)//若旧的(叶子)idr_layer的id全用过了__set_bit(0, &new->bitmap);//那么标记下新(父)idr_layer位图的第0位p = new;//根top指向新的idr_layer}rcu_assign_pointer(idp->top, p);//设置根topidp->layers = layers;//更新层数量v = sub_alloc(idp, &id, pa);//获取idif (v == IDR_NEED_TO_GROW)//该层id号全用完了,必须扩大idr_layer层数量goto build_up;return(v);}static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa){int n, m, sh;struct idr_layer *p, *new;int l, id, oid;unsigned long bm;id = *starting_id; restart:p = idp->top;//获取根topl = idp->layers;//获取层数量l=1pa[l--] = NULL;//pa[1]=NULL,l=0while (1) {n = (id >> (5*l)) & IDR_MASK;//n做处理 属于[0,31]bm = ~p->bitmap;//位图取反m = find_next_bit(&bm, IDR_SIZE, n);//查找n开始能用的位if (m == IDR_SIZE) {//id表满了l++;层数+1oid = id;id = (id | ((1 << (5 * l)) - 1)) + 1;//id或上掩码再+1if (id >= 1 << (idp->layers * 5)) {//需要添加层*starting_id = id;return IDR_NEED_TO_GROW;}p = pa[l];BUG_ON(!p);sh = 5 * (l + 1);if (oid >> sh == id >> sh)continue;elsegoto restart;}if (m != n) {//期望id给用但有可用idsh = 5*l;id = ((id >> sh) ^ n ^ m) << sh;//id设置为可用id}if ((id >= MAX_ID_BIT) || (id < 0))return IDR_NOMORE_SPACE;if (l == 0)//一层层循环计算直到到达叶子处l才为0break;if (!p->ary[m]) {//叶子m为空new = get_from_free_list(idp);//从空闲链表拿一个idr_layerif (!new)return -1;new->layer = l-1;//设置新链表层数rcu_assign_pointer(p->ary[m], new);//叶子m指向新链表p->count++;//使用计数加1}pa[l--] = p;//pa[大]=节点p = p->ary[m];//p=节点->叶子m}pa[l] = p;//pa[小]=叶子return id;}

来个效果图id=4吧


id=32情况(idr_layer13的位图1标记错了)

 

1024情况



四.查找id
1.idr_find

void *idr_find(struct idr *idp, int id){int n;struct idr_layer *p;p = rcu_dereference_raw(idp->top);//获取根topif (!p)return NULL;n = (p->layer+1) * IDR_BITS;//计算最外层的n值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_raw(p->ary[(id >> n) & IDR_MASK]);//一次获取an ... a0  }return((void *)p);}EXPORT_SYMBOL(idr_find);

前面讲过32进制的id值算法


当构建完idr机制之后

id=top->ary[an]->ary[a(n-1)]->....->ary[a0]来获得

 借助图片分析下(idr_layer13的位图标记有错)

 

五idr操作

1. idr_remove  idr_remove_all 移除

void idr_remove(struct idr *idp, int id){struct idr_layer *p;struct idr_layer *to_free;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]) {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);kmem_cache_free(idr_layer_cache, p);}return;}EXPORT_SYMBOL(idr_remove);

移除全部

void idr_remove_all(struct idr *idp){int n, id, max;int bt_mask;struct idr_layer *p;struct idr_layer *pa[MAX_LEVEL];struct idr_layer **paa = &pa[0];n = idp->layers * IDR_BITS;p = idp->top;rcu_assign_pointer(idp->top, NULL);max = 1 << n;id = 0;while (id < max) {while (n > IDR_BITS && p) {n -= IDR_BITS;*paa++ = p;p = p->ary[(id >> n) & IDR_MASK];}bt_mask = id;id += 1 << n;/* Get the highest bit that the above add changed from 0->1. */while (n < fls(id ^ bt_mask)) {if (p)free_layer(p);n += IDR_BITS;p = *--paa;}}idp->layers = 0;}EXPORT_SYMBOL(idr_remove_all);


 

2.idr_replace 替换

void *idr_replace(struct idr *idp, void *ptr, int id){int n;struct idr_layer *p, *old_p;p = idp->top;if (!p)return ERR_PTR(-EINVAL);n = (p->layer+1) * IDR_BITS;id &= MAX_ID_MASK;if (id >= (1 << n))return ERR_PTR(-EINVAL);n -= IDR_BITS;while ((n > 0) && p) {p = p->ary[(id >> n) & IDR_MASK];n -= IDR_BITS;}n = id & IDR_MASK;if (unlikely(p == NULL || !test_bit(n, &p->bitmap)))return ERR_PTR(-ENOENT);old_p = p->ary[n];rcu_assign_pointer(p->ary[n], ptr);return old_p;}EXPORT_SYMBOL(idr_replace);

 

六.idr空闲链表的销毁

idr_destroy

void idr_destroy(struct idr *idp){while (idp->id_free_cnt) {struct idr_layer *p = get_from_free_list(idp);kmem_cache_free(idr_layer_cache, p);}}EXPORT_SYMBOL(idr_destroy);

 

七.用法

1.api函数

void *idr_find(struct idr *idp, int id);//查找id对应的指针int idr_pre_get(struct idr *idp, gfp_t gfp_mask);//分配idr_layer空闲链表int idr_get_new(struct idr *idp, void *ptr, int *id);//获取id,捆绑指针ptrint idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);//起始数值获取id,捆绑指针ptrint idr_for_each(struct idr *idp,int (*fn)(int id, void *p, void *data), void *data);void *idr_get_next(struct idr *idp, int *nextid);void *idr_replace(struct idr *idp, void *ptr, int id);//替换id捆绑的指针void idr_remove(struct idr *idp, int id);//移除idvoid idr_remove_all(struct idr *idp);//移除所有idvoid idr_destroy(struct idr *idp);//销毁idr_layer空闲链表void idr_init(struct idr *idp);//初始化idr

2.大致用法

1.idr_init声明设置idr

2.idr_pre_get分配空闲idr_layer链表

3.id_get_new/idr_get_new_above分配id并将id与指针ptr捆绑

4.利用idr_find根据id获取指针ptr

5.idr_remove/idr_remove_all移除分配的id

6.idr_destroy销毁空闲idr_layer链表

 7.idr_replace替换id

 

 

 

 

 

 

 

 

 

 

原创粉丝点击