Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1204704
  • 博文数量: 185
  • 博客积分: 495
  • 博客等级: 下士
  • 技术积分: 1418
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-02 15:12
个人简介

治肾虚不含糖,专注内核性能优化二十年。 https://github.com/KnightKu

文章分类

全部博文(185)

文章存档

2019年(1)

2018年(12)

2017年(5)

2016年(23)

2015年(1)

2014年(22)

2013年(82)

2012年(39)

分类:

2012-11-15 10:36:51

原文地址:linux機制之IDR 作者:steven_miao

linux機制之IDR

一.前言

linux中有idr,關於idr的用處並不是清楚。查看網上所述知所谓IDR,其实就是和身份证的含义差不多,我们知道,每个人有一个身份证,身份证只是一串数字,从数字,我们就能知道这个人的信息。同样道理,idr的要完成的任务是给要管理的对象分配一个数字,可以通过这个数字找到要管理的对象。類似于為內核任何對象創建一個普通的id,通過該id可以很快的索引到該對象。在該文中將詳細講述linuxidr的具體實現。

二.idr數據結構

linux中關於idr有兩個很重要的數據結構1. struct idr 2. struct idr_layer,下面具體來看這兩個數據結構

struct _layer {

         unsigned long           bitmap;   --標示idary中的位置

         struct idr_layer        *ary[1<  --用於保存對象的地址

         int                       count;    --當該值為0時,釋放他

         int                       layer;      --距離葉子的距離

         struct rcu_head                 rcu_head;  --這個不管

};

struct idr {

         struct idr_layer *top;       --標示使用的idr_layer

         struct idr_layer *id_free;    --連接沒有使用的idr_layer

         int                layers;         --拒絕幷發

         int                id_free_cnt;    --空閒的idr_layer計數

         spinlock_t           lock;

};

三.定義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)

通過宏定義定義一個名為nameidr實體。

四.具體使用idr

在開始idr具體使用之前,需要說明幾個宏的意思

32位機下IDR_BITS5

#define IDR_SIZE (1 <<)   =320x20

#define IDR_MASK ((1 << IDR_BITS)-1) =310x1F

#define MAX_ID_SHIFT (sizeof(int)*8 - 1)  =31

#define MAX_ID_BIT (1U << MAX_ID_SHIFT) =0x100000000

#define MAX_ID_MASK (MAX_ID_BIT - 1)  =0xFFFFFFFF

#define MAX_LEVEL (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS =7

#define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL =14

上面的宏定義中比較難以理解的是MAX_LEVEL爲什麽是7,其實很好理解,由於我們是32位機,其可以使用的id號時0xFFFFFFFF,而我們idr_layer一層最多為0x1F,故最少需要7level才能將一個id號瓜分完成。也許空口在這兒說,可能一層霧水。下面根據具體的示例就好理解很多。

4.1 idr_pre_get

int(struct idr *idp, gfp_t gfp_mask)

{

         while (idp->id_free_cnt < IDR_FREE_MAX) {

                   struct idr_layer *new;

                   new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);

                   if (new == NULL)

                            return (0);

                   (idp, new);

         }

         return 1;

}

上面的重點是move_to_free_list

move_to_free_list-> __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++;

}

上面的函數很好理解,下圖會很好的闡述上面函數的意思


idr_pre_get結束時id_free_count=14.其實該函數的本質是開闢idr_layer內存。

4.2 idr_get_new

分配一個新的idr entry,返回值存儲在id中。

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;

}

下面就是按圖索驥,一步一步去分析idr的內幕。

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) {

                   rcu_assign_pointer(pa[0]->ary[id & IDR_MASK], (struct idr_layer *)ptr);

                   pa[0]->count++;

                   idr_mark_full(pa, id);

         }

         return id;

}

也許單看這些函數會很枯燥,我們來假設一情況,結合具體的內容會好理解很多。在某一驅動中,調用idr_pre_get后第一次調用下面函數。以starting_id0

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;   --p=NULL

         layers = idp->layers; -- layers = 0

         if (unlikely(!p)) {

                   if (!(p = get_from_free_list(idp))) –從上面的id_free上卸下一個idr_layer,第一次使用。

                            return -1;

                   p->layer = 0;

                   layers = 1;

         }

//id0時,直接跳過下面的while

         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); --idp->top = p;

         idp->layers = layers;    --idp->layers = 1

         v = sub_alloc(idp, &id, pa); --這個是個關鍵

         if (v == IDR_NEED_TO_GROW)

                   goto build_up;

         return(v);

}

free_list上獲得一個未使用的idr_layer

static struct idr_layer *get_from_free_list(struct idr *idp)

{

         struct idr_layer *p;

         if ((p = idp->id_free)) {

                   idp->id_free = p->ary[0];

                   idp->id_free_cnt--;

                   p->ary[0] = NULL;

         }

         return(p);

}

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;   --id = 0

 restart:

         p = idp->top;    --p應該為第一個可用的idr_layer

         l = idp->layers;  --l = 1;

         pa[l--] = NULL; 

         while (1) {

                   n = (id >> (IDR_BITS*l)) & IDR_MASK;  --n = 0

                   bm = ~p->bitmap;               --bm = 0xFFFFFFFF

                   m = find_next_bit(&bm, IDR_SIZE, n); --查找第一個bit1的位,這裡m = 1

                   if (m == IDR_SIZE) {

                            l++;

                            oid = id;

                            id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;

                                     if (!(p = pa[l])) {

                                     *starting_id = id;

                                     return IDR_NEED_TO_GROW;

                            }

                            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 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] = idr_layer1

         return id;

}

idr_get_new_above_int函數中

rcu_assign_pointer(pa[0]->ary[id & IDR_MASK], );

pa[0]->count++;

idr_mark_full(pa, id); 

*idr_lay1->ary[0] = (struct idr_layer *)ptr , idr_lay1->count = 1;同時將bitmap中對應idbit位設置為1.

上面主要是針對第一次調用idr_get_new情況的分析。

 

 

阅读(810) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~