程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> idr機制(32叉樹)

idr機制(32叉樹)

編輯:C++入門知識

一.結構體 1.idr結構體 [cpp]  struct idr {       struct idr_layer __rcu *top;    //idr_layer頂層,32叉樹的根       struct idr_layer *id_free;      //指向idr_layer的空閒鏈表       int layers;     //idr_layer的層數量       int id_free_cnt;    //idr_layer空閒鏈表中剩余的idr_layer個數       spinlock_t  lock;   };   2.idr_layer結構體 [cpp  struct idr_layer {       unsigned long   bitmap; //標記位圖,標記使用情況       struct idr_layer __rcu  *ary[1<<IDR_BITS];        //子idr_layer數組ary[32]       int count;  //ary數組使用情況       int layer;  //層號       struct rcu_head rcu_head;   };   在32位系統中IDR_BITS的取值為5 [cpp]   #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的初始化 [cpp]   #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 [cpp]   int idr_pre_get(struct idr *idp, gfp_t gfp_mask)   {       while (idp->id_free_cnt < IDR_FREE_MAX) { //IDR_FREE_MAX=14           struct 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 [cpp]  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_list       spin_unlock_irqrestore(&idp->lock, flags);   }   __move_to_free_list [cpp]   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 [cpp]  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 [cpp]  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鏈表頭 [cpp]  static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)   {       struct idr_layer *pa[MAX_LEVEL];    //MAX_LEVEL=7       int id;       id = idr_get_empty_slot(idp, starting_id, pa);  //-->idr_get_empty_slot       if (id >= 0) {           rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr           //pa[0]->ary[0]=ptr 也就是idr_layer14->ary[0]=ptr           pa[0]->count++;  //idr_layer14->count++           idr_mark_full(pa, id);  //設置其位圖-->走完0過場的效果見圖c       }       return id;   }   idr_get_empty_slot [cpp]  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;   //按常規出牌吧,假設這個為0   build_up:       p = idp->top;    //根top指向的idr_layer NULL       layers = idp->layers;    //獲取layers層數量(0)       if (unlikely(!p)) { //第一次運行idp->top=NULL,所以if條件為真,執行if分支的結果參考 圖A           if (!(p = get_from_free_list(idp))) //>>>1-->get_from_free_list 從根free中獲取一個idr_layer14               return -1;           p->layer = 0;    //指定idr_layer14的層號為0           layers = 1; //layers層數量設為1       }       //layers<6 && id>=2^(layers*5) 看需不需要增加層數 見圖B       while ((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_layer14       idp->layers = layers;    //設置更新idr->layers層數量   //----------------------------------------------分割線----------------------------------------------   //以上部分主要處理layer相關,以下部分主要處理id相關       v = sub_alloc(idp, &id, pa);    //>>>2-->sub_alloc       if (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 [cpp]   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_layer           idp->id_free = p->ary[0]; //idr空閒鏈表指針指向第二個idr_layer           idp->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 [cpp]  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;    //根top       l = idp->layers; //l=1       pa[l--] = NULL; //p[1]=NULL;l=0       while (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;               else                   goto 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_layer14       return id;   }   >>>find_next_bit [cpp] view plaincopy #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 [cpp]   ENTRY(_find_next_bit_le)           teq r1, #0           beq 3b           ands    ip, r2, #7           beq 1b          @ If new byte, goto old routine    ARM(       ldrb    r3, [r0, r2, lsr #3]    )    THUMB(     lsr r3, r2, #3      )    THUMB(     ldrb    r3, [r0, r3]        )           movs    r3, r3, lsr ip      @ shift off unused bits           bne .L_found           orr r2, r2, #7      @ if zero, then no bits here           add r2, r2, #1      @ align bit pointer           b   2b          @ loop for next bit   ENDPROC(_find_next_bit_le)   .L_found找到合適的跳轉 [cpp]   .L_found:   #if __LINUX_ARM_ARCH__ >= 5           rsb r0, r3, #0           and r3, r3, r0           clz r3, r3           rsb r3, r3, #31           add r0, r2, r3   #else           tst r3, #0x0f           addeq   r2, r2, #4           movne   r3, r3, lsl #4           tst r3, #0x30           addeq   r2, r2, #2           movne   r3, r3, lsl #2           tst r3, #0x40           addeq   r2, r2, #1           mov r0, r2   #endif           cmp r1, r0          @ Clamp to maxbit           movlo   r0, r1           mov pc, 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走了過場,下面分析下其他情況 [cpp]   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);  //獲取id       if (id >= 0) {           rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],(struct idr_layer *)ptr);           //pa[0]->ary[id]=ptr           pa[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;    //獲取根top       layers = idp->layers;    //獲取層數量 layers=1       if (unlikely(!p)) { //FALSE           if (!(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層的,所以之前的層的層號需要+1               continue;    //層數量也需要加1            }           if (!(new = get_from_free_list(idp))) { //空閒鏈表中獲取新的idr_layer               spin_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_layer           new->count = 1;  //新的idr_layer計數加1           new->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); //設置根top       idp->layers = layers;    //更新層數量       v = sub_alloc(idp, &id, pa);    //獲取id       if (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;    //獲取根top       l = idp->layers; //獲取層數量l=1       pa[l--] = NULL; //pa[1]=NULL,l=0       while (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++;    層數+1               oid = id;               id = (id | ((1 << (5 * l)) - 1)) + 1; //id或上掩碼再+1                   if (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;               else                   goto restart;           }           if (m != n) {   //期望id給用但有可用id               sh = 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才為0               break;           if (!p->ary[m]) {    //葉子m為空               new = get_from_free_list(idp);  //從空閒鏈表拿一個idr_layer               if (!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 [cpp]   void *idr_find(struct idr *idp, int id)   {       int n;       struct idr_layer *p;          p = rcu_dereference_raw(idp->top);   //獲取根top       if (!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 移除 [cpp]  www.2cto.com 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);   移除全部 [cpp]   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 替換 [cpp]   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 [cpp]   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函數 [cpp]   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,捆綁指針ptr   int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);    //起始數值獲取id,捆綁指針ptr   int 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);   //移除id   void idr_remove_all(struct idr *idp);   //移除所有id   void 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  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved