Linux中的IDR機制


# Linux中的IDR機制

背景

最近在學習 Linux的i2c子系統,看到代碼中有關於IDR的調用。了解了一下有關的文檔,發現是用來管理指針(對象實例)。

//based on linux V3.14 source code

reference:

概述

系統許多資源都用整數ID來標識,如進程ID、文件描述符ID、IPC ID等;資源信息通常存放在對應的數據結構中(如進程信息存放在task_struct中、ipc信息存放在ipc_perm中),id與數據結構的關聯機制有不同的實現,idr機制是其中的一種。

idr,id radix的縮寫。idr主要用於建立id與指針(指向對應的數據結構)之間的對應關系。idr用類基數樹結構來構造一個稀疏數組,以id為索引找到對應數組元素,進而找到對應的數據結構指針。

IDR機制在Linux內核中指的是整數ID管理機制。實質上來講,這就是一種將一個整數ID號和一個指針關聯在一起的機制。這個機制最早在03年2月加入內核,當時作為POSIX定時器的一個補丁。現在,內核中很多地方都可以找到它的身影。

用到idr機制的主要有:IPC id(消息隊列id、信號量id、共享內存id等),磁盤分區id(sda中數字部分)等。

IDR機制產生的背景原理:

IDR機制適用在那些需要把某個整數和特定指針關聯在一起的地方。例如,在IIC總線中,每個設備都有自己的地址,要想在總線上找到特定的設備,就必須要先發送設備的地址。當適配器要訪問總線上的IIC設備時,首先要知道它們的ID號,同時要在內核中建立一個用於描述該設備的結構體,和驅動程序。將ID號和設備結構體結合起來,如果使用數組進行索引,一旦ID號很大,則用數組索引會占據大量內存空間。這顯然不可能。或者用鏈表,但是,如果總線中實際存在的設備很多,則鏈表的查詢效率會很低。此時,IDR機制應運而生,可以很方便的將整數和指針關聯起來,並且具有很高的搜索效率(內部采用radix樹實現)。

相關結構體

struct idr {
    struct idr_layer __rcu *hint;        //最近一個存儲指針數據的的idr_layer結構
    struct idr_layer __rcu *top;            //idr的idr_layer樹頂層,樹的根
    struct idr_layer *id_free;    //指向idr_layer的空閑鏈表
    int layers;        //idr樹中的idr_layer層數量
    int id_free_cnt;    //idr_layer空閑鏈表中剩余的idr_layer個數 
    int cur;            //current pos for cyclic allocation
    spinlock_t lock;
};


struct idr_layer {
    int prefix;     //the ID prefix of this idr_layer 
    DECLARE_BITMAP(bitmap, IDR_SIZE); //標記位圖,標記該idr_layer的ary數組使用情況
    //該數組用於保存具體的指針數據或者指向子idr_layer結構,大小為1<<8=256項
    struct idr_layer __rcu *ary[1<<IDR_BITS]; 
    int count;        //ary數組使用計數
    int layer;        //層號
    struct rcu_head rcu_head;
};

idr初始化

在start_kernel函數中調用idr_init_cache()對idr進行相應的初始化。創建一個slab cache,為后邊分配idr_layer結構。


static struct kmem_cache *idr_layer_cache;
void __init idr_init_cache(void)
{
    idr_layer_cache = kmem_cache_create("idr_layer_cache",sizeof(struct idr_layer), 0, SLAB_PANIC, NULL);
}

idr的使用

1.idr的初始化

(1)宏定義並且初始化一個名為name的idr:

#define DEFINE_IDR(name) struct idr name = IDR_INIT(name)
#define IDR_INIT(name)     \
        { \
            .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
        }

(2)動態初始化idr:

void idr_init(struct idr *idp)
{
    memset(idp, 0, sizeof(struct idr));
    spin_lock_init(&idp->lock);
}

2.分配idr的空閑idr_layer鏈表

static inline int __deprecated idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
    return __idr_pre_get(idp, gfp_mask);
}

//注意該函數會導致睡眠,因此不應該用鎖保護,函數實現如下
#define MAX_IDR_SHIFT    (sizeof(int) * 8 - 1)
#define MAX_IDR_LEVEL    ((MAX_IDR_SHIFT + IDR_BITS - 1) / IDR_BITS)
#define MAX_IDR_FREE    (MAX_IDR_LEVEL * 2)
//32位系統下,MAX_IDR_SHIFT=31,則MAX_IDR_LEVEL=(31+8-1)/8=4,則MAX_IDR_FREE=4*2=8
int __idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
    //32位系統下,MAX_IDR_FREE=8,所以idr有最多8個處於free狀態的idr_layer內存空間
    while (idp->id_free_cnt < MAX_IDR_FREE) {
        struct idr_layer *new;
        //通過slab高速緩存分配idr_layer內存空間
        new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);
        if (new == NULL)
            return (0);
        //將idr_layer結構鏈入idr空閑可用鏈表中
        move_to_free_list(idp, new);
    }
    return 1;
}

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);
    spin_unlock_irqrestore(&idp->lock, flags);
}

static void __move_to_free_list(struct idr *idp, struct idr_layer *p)
{
     //p代指新創建的id_free成員idr_layer結構。
    //當idp->id_free = NULL時(剛初始化),p->ary[0] = idp->id_free = NULL。
    //當idp->id_free不為NULL的時候,就表示新創建的idr_layer的ary[0]指向之前的idp->id_free指向的成員,然后再將idp->id_free指向新的成員。最終8個idr_layer都鏈入鏈表,結構如下:
    /* 
    idp->id_free -> p8
    p8->ary[0] -> p7 
    p7->ary[0] -> p6 
    ... 
    ... 
    p1->ary[0] -> NULL
     */
    p->ary[0] = idp->id_free;
    idp->id_free = p;
    idp->id_free_cnt++;
} 

3.分配id號並將id號和指針關聯

idr有一種比較簡單的理解方式,因為之前的IDR_BITS=5,現在在3.14內核中IDR_BITS=8,所以現在它就是一種256進制的數,滿256,向前進一位。

假設當前我們是兩層結構,top指向256叉樹的根,top下面管理256個葉子層的idr_layer。葉子層idr_layer的ary數組元素是用來指向目標obj的。那么兩層總共可以管理256256=65536個obj。同樣道理三層可以最多管理256256*256=16M個obj。

static inline int idr_get_new(struct idr *idp, void *ptr, int *id)
{
    return __idr_get_new_above(idp, ptr, 0, id);
}

//參數idp是之前通過idr_init()初始化的idr指針,或者DEFINE_IDR宏定義的指針。
//參數ptr是和ID號相關聯的指針。
//參數id由內核自動分配的ID號,輸出參數。
//參數start_id是起始ID號。
int __idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id)
{
    struct idr_layer *pa[MAX_IDR_LEVEL + 1];
    int rv;
    
    //在該idr的idr_layer樹中分配一個合適的id,並且分配的idr_layer路徑記錄在pa數組中
    rv = idr_get_empty_slot(idp, starting_id, pa, 0, idp);
    if (rv < 0)
        return rv == -ENOMEM ? -EAGAIN : rv;
    
    //關聯ptr和id    
    idr_fill_slot(idp, ptr, rv, pa);
    *id = rv;
    return 0;
}

static int idr_get_empty_slot(struct idr *idp, int starting_id,
                            struct idr_layer **pa, gfp_t gfp_mask,
                            struct idr *layer_idr)
{
    struct idr_layer *p, *new;
    int layers, v, id;
    unsigned long flags;
        
    id = starting_id;//starting_id=0
build_up:
    //第一次申請id號時,根top指向的idr_layer為NULL
    p = idp->top;
    //第一次申請id號時,layers層數量idp->layers為0
    layers = idp->layers;
    //若top指針為NULL,則先設置top指針
    if (unlikely(!p)) {
        //從idr空閑idr_layer鏈表中獲取最后一個鏈入鏈表的idr_layer結構,一般為idr_layer8
        //沒有的話,則重新分配一個idr_layer結構
        if (!(p = idr_layer_alloc(gfp_mask, layer_idr)))
            return -ENOMEM;
        p->layer = 0;//指定該idr_layer層號為0
        layers = 1; //layers層數量設為1,此時只有根idr_layer,即idr_layer8
    }

    //如果起始的id號超過該idr中設定的idr_layer層數所能設置的id號最大值,則增加idr中的idr_layer樹
    while (id > idr_max(layers)) {
        layers++;//idr層數加1
        //count為0,表示該idr_layer結構沒有子節點???
        if (!p->count) {
            /* special case: if the tree is currently empty,
            * then we grow the tree by moving the top node upwards.
            */
            p->layer++;
            WARN_ON_ONCE(p->prefix);
            continue;
        }
    
        //從layer_idr的空閑鏈表中分配一個idr_layer結構,或者從內存中分配一個idr_layer結構
        if (!(new = idr_layer_alloc(gfp_mask, layer_idr))) {
            //若分配失敗,top指針指向的idr_layer結構全部要重新初始化,並移到idr的free鏈表中,返回錯誤碼
            spin_lock_irqsave(&idp->lock, flags);
            for (new = p; p && p != idp->top; new = p) {
                p = p->ary[0];
                new->ary[0] = NULL;
                new->count = 0;
                bitmap_clear(new->bitmap, 0, IDR_SIZE);
                __move_to_free_list(idp, new);
            }
            spin_unlock_irqrestore(&idp->lock, flags);
            return -ENOMEM;
        }
    
        //新分配的new節點鏈入top所指向的idr_layer鏈表中,變成p的父節點
        new->ary[0] = p;
        //count設為1表示有一個子節點,即ary數組的使用計數
        new->count = 1;
        //設置層號
        new->layer = layers-1;
        new->prefix = id & idr_layer_prefix_mask(new->layer);
        //如果p的位圖滿,則設置p的父節點new的位圖第0位為1,因為new的ary數組0項指向p
        if (bitmap_full(p->bitmap, IDR_SIZE))
            __set_bit(0, new->bitmap);
        //設置p指向新加入的idr_layer節點
        p = new;
    }
    
    //設置根top指針
    rcu_assign_pointer(idp->top, p);
    //設置更新idr->layers層數量
    idp->layers = layers;
    //從idr的top指針指向的idr_layer樹中獲得id號,分配路徑記錄在pa數組中
    v = sub_alloc(idp, &id, pa, gfp_mask, layer_idr);
    if (v == -EAGAIN)
        goto build_up;
    return(v);
}

static struct idr_layer *idr_layer_alloc(gfp_t gfp_mask, struct idr *layer_idr)
{
    struct idr_layer *new;
        
    //從idr空閑idr_layer鏈表中獲取第一個idr_layer
    if (layer_idr)
        return get_from_free_list(layer_idr);
        
    //如果idr空閑idr_layer鏈表中已經沒有idr_layer結構,則通過slab高速緩存分配一個idr_layer結構返回
    new = kmem_cache_zalloc(idr_layer_cache, gfp_mask | __GFP_NOWARN);
    if (new)
        return new;
     
    //如果上邊內存分配失敗,則從idr_preload_head數組中分配一個可用的idr_layer結構,參考idr_preload()
    if (!in_interrupt()) {//不能在中斷上下文中,要在進程上下文中
        //禁止內核強占
        preempt_disable();
        //從idr_preload_head數組分配一個idr_layer結構
        new = __this_cpu_read(idr_preload_head);
        if (new) {
            //將idr_preload_head指向下一個idr_layer結構
            __this_cpu_write(idr_preload_head, new->ary[0]);
            //遞減計數
            __this_cpu_dec(idr_preload_cnt);
            //將new從鏈表中刪除
            new->ary[0] = NULL;
        }
        //使能內核搶占
        preempt_enable();
        if (new)
            return new;
    }
    
    //若上邊分配均失敗,則再次嘗試從slab高速緩存分配idr_layer結構
    return kmem_cache_zalloc(idr_layer_cache, gfp_mask);
}

static struct idr_layer *get_from_free_list(struct idr *idp)
{
    struct idr_layer *p;
    unsigned long flags;
         
    spin_lock_irqsave(&idp->lock, flags);
    //從idr的free鏈表獲取一個空閑idr_layer 
    if ((p = idp->id_free)) {
        idp->id_free = p->ary[0];//idr空閑鏈表指針指向第二個idr_layer
        idp->id_free_cnt--;//idr的空閑idr_layer個數減1
        p->ary[0] = NULL;//將該idr_layer從id_free鏈表中刪除
    }
    spin_unlock_irqrestore(&idp->lock, flags);
    return(p);
}

static int idr_max(int layers)
{
    //取layers*8和31的小值
    //layers小於4層時,id取值可達到2^(layers*8)-1
    //layers大於等於4層時,id取值最大取值設置為2^31-1
    int bits = min_t(int, layers * IDR_BITS, MAX_IDR_SHIFT);

    return (1 << bits) - 1;
}

static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa,
                    gfp_t gfp_mask, struct idr *layer_idr)
{
    int n, m, sh;
    struct idr_layer *p, *new;
    int l, id, oid;
     
    id = *starting_id;//起始id號為0
restart:
    //找到idr的根top指向的idr_layer
    p = idp->top;
    //idr中的layers層數量
    l = idp->layers;
    pa[l--] = NULL;

    while (1) {
        //n=(id>>8*l) & 0xFF,計算對應的n值,為該layer中的哪個位置。
        //若idr中只有1層idr_layer的話,則n值范圍為0~255
        n = (id >> (IDR_BITS*l)) & IDR_MASK;
        //從位圖中的第n位開始,查找第一個不為0的位,表示該位可用,為1的位表示已經被使用
        m = find_next_zero_bit(p->bitmap, IDR_SIZE, n);
    
        //如果找到的空閑位置m等於IDR_SIZE(即256),表示該idr_layer的位圖已經滿了,
        //如果該idr_layer有子節點,並且對應該子節點的bit也為1了,表示該子節點的位圖也滿了,
        //則需要為該idr增加idr_layer結構
        if (m == IDR_SIZE) {
            l++;//層數遞加
            oid = id;
            //重新計算id,該id為被增長之后的新值,即新值根據層數右移8*l位
            id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
        
            //如果重新計算過的id值,大於目前idr中的idr_layer層數所能設置的最大id值
            //則說明該idr不能分配id值了,需要增加idr中的idr_layer層數,出錯返回
            if (id >= 1 << (idp->layers * IDR_BITS)) {
                *starting_id = id;
                return -EAGAIN;
            }
            p = pa[l];
            BUG_ON(!p);
        
            //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;
        }
    
        //期望的n值被占用,但可找到可用的m值,重新計算id值
        //示例:如果id=0x0A01,則0x0A=10代表第一級的idr_layer的ary數組的索引,0x01代表下一級的ary數組索引,最終ptr數據指針就保存在下一級的ary[0x01]處。
        if (m != n) {
            sh = IDR_BITS*l;
            id = ((id >> sh) ^ n ^ m) << sh;
        }
    
        //id超過所能分配的最大值(1 << 31)或者小於0,則出錯返回
        if ((id >= MAX_IDR_BIT) || (id < 0))
            return -ENOSPC;
    
        //一層層循環計算直到到達葉子節點處l才為0,然后才跳出循環
        if (l == 0)
            break;
    
        //p的葉子節點m為空
        if (!p->ary[m]) {
            //從idr空閑鏈表取出一個idr_layer結構,沒有則重新分配一個idr_layer結構
            new = idr_layer_alloc(gfp_mask, layer_idr);
            if (!new)
                return -ENOMEM;
            new->layer = l-1;//設置新節點的所在層數
            new->prefix = id & idr_layer_prefix_mask(new->layer);
            rcu_assign_pointer(p->ary[m], new);//父節點p的葉子m指向new
            p->count++;//父節點p的使用計數加1,即表示有多少個字節點
        }
        pa[l--] = p;//將中間的節點存入pa對應的數組中
        p = p->ary[m];//p指向下一個葉子節點
    }
    //執行到這里,l=0。p指向最終的葉子節點。pa數組記錄id存放在idr的idr_layer樹路徑,最終要存放id的idr_layer葉子節點存放在pa[0]中。
    //p為最終要存放數據指針ptr的idr_layer,存入pa[0]數組中,后邊會在該idr_layer的[id & IDR_MASK]處存放數據指針ptr
    pa[l] = p;
    return id;
}

static void idr_fill_slot(struct idr *idr, void *ptr, int id,struct idr_layer **pa)
{
    //pa數組記錄id存放在idr的idr_layer樹路徑,最終要存放id的idr_layer葉子節點存放在pa[0]中。
    //將pa[0]存儲的idr_layer結構存入hint域,用於下次快速查找,相當與cache。
    rcu_assign_pointer(idr->hint, pa[0]);
    //將數據指針地址存入查找到的idr_layer葉子節點的ary數組的[id&IDR_MASK]處
    rcu_assign_pointer(pa[0]->ary[id & IDR_MASK], (struct idr_layer *)ptr);
    //該idr_layer結構的使用計數加1
    pa[0]->count++;
    //標志該節點已被使用的bitmap位
    idr_mark_full(pa, id);
}

static void idr_mark_full(struct idr_layer **pa, int id)
{
    struct idr_layer *p = pa[0];
    int l = 0;
     
    //根據id設置該idr_layer的位圖,在該位圖的第id位設為1
    __set_bit(id & IDR_MASK, p->bitmap);
    
    //若該idr_layer的整個位圖為滿,則標志該idr_layer的父節點對應的位為1
    while (bitmap_full(p->bitmap, IDR_SIZE)) {
        //找到該idr_layer的父節點
        if (!(p = pa[++l]))
            break;
        //因為是該idr_layer的父節點,所以id對應的父節點應該右移8位,設置位圖
        id = id >> IDR_BITS;
        __set_bit((id & IDR_MASK), p->bitmap);
    }
}

4.查找id對應的指針

要想找到obj的指針,必須根據id,一路尋找到葉子層。這里假設為2層的話,若id=266,則266/256 = 1,所以從top---->top->ary[1],我們就找到了葉子節點C。266&IDR_MASK = 10,所以C的ary[10]指向管理的obj。

(1)用前面的256進制方法理解就是266 = 1*256+10,所以,top->ary[1]->ary[10]指向obj。

(2)同樣我們可以求id=27對應的obj,27=0*256+27,所以top->ary[0]->ary[27]指向obj。

static inline void *idr_find(struct idr *idr, int id)
{
    //hint保留上次操作過的idr_layer指針
    struct idr_layer *hint = rcu_dereference_raw(idr->hint);
    
    //比較檢查是否為當前id對應的idr_layer,是的話直接從該idr_layer的ary數組返回數據
    if (hint && (id & ~IDR_MASK) == hint->prefix)
        return rcu_dereference_raw(hint->ary[id & IDR_MASK]);
    
    //否則從idr樹中查找    
    return idr_find_slowpath(idr, id);
}

void *idr_find_slowpath(struct idr *idp, int id)
{
    int n;
    struct idr_layer *p;
        
    if (id < 0)
        return NULL;
    
    //找到該idr的top指針    
    p = rcu_dereference_raw(idp->top);
    if (!p)
        return NULL;
    
    //top指向的idr_layer的層號加1,就是整個idr的idr_layer樹的層數
    n = (p->layer+1) * IDR_BITS;
     
    //如果id號超過該idr中設定的idr_layer層數所能設置的id號最大值,則返回NULL
    if (id > idr_max(p->layer + 1))
        return NULL;
    BUG_ON(n == 0);
    
    //從樹頂部top,往樹葉查找,取出id對應的數組中的數據指針
    //假設為兩層,則id值高8位保存上一級的idr_layer的ary數組索引,低8位保存下一級的idr_layer的ary數組索引
    while (n > 0 && p) {
        n -= IDR_BITS;
        BUG_ON(n != p->layer*IDR_BITS);
        p = rcu_dereference_raw(p->ary[(id >> n) & IDR_MASK]);
    }
    return((void *)p);
}

5.idr_replace替換id

void *idr_replace(struct idr *idp, void *ptr, int id)
{
    int n;
    struct idr_layer *p, *old_p;
     
    if (id < 0)
        return ERR_PTR(-EINVAL);
        
    //找到該idr的top指針    
    p = idp->top;
    if (!p)
        return ERR_PTR(-EINVAL);
    
    //根據idr層數,設置對應的位數    
    n = (p->layer+1) * IDR_BITS;
        
    if (id >= (1 << n))
        return ERR_PTR(-EINVAL);
    
    //從樹頂部top,往樹葉查找,取出id對應的數組中的數據指針    
    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);
    
    //對應id的ary數組,指針替換
    old_p = p->ary[n];
    rcu_assign_pointer(p->ary[n], ptr);
        
    return old_p;
}

6.idr_remove/idr_remove_all移除分配的id

void idr_remove(struct idr *idp, int id)
{
    struct idr_layer *p;
    struct idr_layer *to_free;
        
    if (id < 0)
        return;
    
    //釋放id對應的idr_layer路徑的空間
    sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);
    if (idp->top && idp->top->count == 1 && (idp->layers > 1) && idp->top->ary[0]) {
    /*
    * Single child at leftmost slot: we can shrink the tree.
    * This level is not needed anymore since when layers are
    * inserted, they are inserted at the top of the existing
    * tree.
    */
        to_free = idp->top;
        p = idp->top->ary[0];
        rcu_assign_pointer(idp->top, p);
        --idp->layers;
        to_free->count = 0;
        bitmap_clear(to_free->bitmap, 0, IDR_SIZE);
        free_layer(idp, to_free);
    }
    while (idp->id_free_cnt >= MAX_IDR_FREE) {
        p = get_from_free_list(idp);
        /*
        * Note: we don't call the rcu callback here, since the only
        * layers that fall into the freelist are those that have been
        * preallocated.
        */
        kmem_cache_free(idr_layer_cache, p);
    }
    return;
}

static void sub_remove(struct idr *idp, int shift, int id)
{
    struct idr_layer *p = idp->top;
    struct idr_layer **pa[MAX_IDR_LEVEL + 1];
    struct idr_layer ***paa = &pa[0];
    struct idr_layer *to_free;
    int n;
        
    *paa = NULL;
    *++paa = &idp->top;
    
    //循環將存儲id的idr_layer樹路徑保存在數組paa中    
    while ((shift > 0) && p) {
        n = (id >> shift) & IDR_MASK;
        __clear_bit(n, p->bitmap);
        *++paa = &p->ary[n];
        p = p->ary[n];
        shift -= IDR_BITS;
    }
    
    //遍歷paa數組,將數組中的idr_layer結構都釋放掉
    n = id & IDR_MASK;
    if (likely(p != NULL && test_bit(n, p->bitmap))) {
        __clear_bit(n, p->bitmap);
        rcu_assign_pointer(p->ary[n], NULL);
        to_free = NULL;
        while(*paa && ! --((**paa)->count)){
            if (to_free)
                free_layer(idp, to_free);
            to_free = **paa;
            **paa-- = NULL;
        }
        if (!*paa)
            idp->layers = 0;
        if (to_free)
            free_layer(idp, to_free);
    } else
        idr_remove_warning(id);
}

7.idr_destroy銷毀空閑idr_layer鏈表

void idr_destroy(struct idr *idp)
{
    __idr_remove_all(idp);
    
    //遍歷idr的free鏈表中的idr_layer結構,依次取出並釋放內存空間 
    while (idp->id_free_cnt) {
        struct idr_layer *p = get_from_free_list(idp);
        kmem_cache_free(idr_layer_cache, p);
    }
}

void __idr_remove_all(struct idr *idp)
{
    int n, id, max;
    int bt_mask;
    struct idr_layer *p;
    struct idr_layer *pa[MAX_IDR_LEVEL + 1];
    struct idr_layer **paa = &pa[0];
        
    n = idp->layers * IDR_BITS;
    //取出idr的top指針
    p = idp->top;
    //top指針置為NULL
    rcu_assign_pointer(idp->top, NULL);
    //計算該idr中設定的idr_layer層數所能設置的id號最大值
    max = idr_max(idp->layers);
        
    id = 0;
    while (id >= 0 && 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(idp, p);
            n += IDR_BITS;
            p = *--paa;
        }
    }
    idp->layers = 0;
}

結構圖如下:

img

img


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM