国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
基數(shù)(radix)樹 - 嵌入式系統(tǒng)書撰寫

基數(shù)(radix)樹


Linux基數(shù)樹(radix tree)是將指針與long整數(shù)鍵值相關(guān)聯(lián)的機制,它存儲有效率,并且可快速查詢,用于指針與整數(shù)值的映射(如:IDR機制)、內(nèi)存管理等。
IDR(ID Radix)機制是將對象的身份鑒別號整數(shù)值ID與對象指針建立關(guān)聯(lián)表,完成從ID與指針之間的相互轉(zhuǎn)換。IDR機制使用radix樹狀結(jié)構(gòu)作為由id進行索引獲取指針的稀疏數(shù)組,通過使用位圖可以快速分配新的ID,IDR機制避免了使用固定尺寸的數(shù)組存放指針。IDR機制的API函數(shù)在lib/idr.c中實現(xiàn),這里不加分析。
Linux radix樹最廣泛的用途是用于內(nèi)存管理,結(jié)構(gòu)address_space通過radix樹跟蹤綁定到地址映射上的核心頁,該radix樹允許內(nèi)存管理代碼快速查找標識為dirty或writeback的頁。Linux radix樹的API函數(shù)在lib/radix-tree.c中實現(xiàn)。


(1)radix樹概述


radix樹是通用的字典類型數(shù)據(jù)結(jié)構(gòu),radix樹又稱為PAT位樹(Patricia Trie or crit bit tree)。Linux內(nèi)核使用了數(shù)據(jù)類型unsigned long的固定長度輸入的版本。每級代表了輸入空間固定位數(shù)。
radix tree是一種多叉搜索樹,樹的葉子結(jié)點是實際的數(shù)據(jù)條目。每個結(jié)點有一個固定的、2^n指針指向子結(jié)點(每個指針稱為槽slot),并有一個指針指向父結(jié)點。

Linux內(nèi)核利用radix樹在文件內(nèi)偏移快速定位文件緩存頁,圖4是一個radix樹樣例,該radix樹的分叉為4(22),樹高為4,樹的每個葉子結(jié)點用來快速定位8位文件內(nèi)偏移,可以定位4x4x4x4=256頁,如:圖中虛線對應(yīng)的兩個葉子結(jié)點的路徑組成值0x00000010和0x11111010,指向文件內(nèi)相應(yīng)偏移所對應(yīng)的緩存頁。


圖4 一個四叉radix樹


Linux radix樹每個結(jié)點有64個slot,與數(shù)據(jù)類型long的位數(shù)相同,圖1顯示了一個有3級結(jié)點的radix樹,每個數(shù)據(jù)條目(item)可用3個6位的鍵值(key)進行索引,鍵值從左到右分別代表第1~3層結(jié)點位置。沒有孩子的結(jié)點在圖中不出現(xiàn)。因此,radix樹為稀疏樹提供了有效的存儲,代替固定尺寸數(shù)組提供了鍵值到指針的快速查找。


圖1 一個3級結(jié)點的radix樹及其鍵值表示


(2)radix樹slot數(shù)


Linux內(nèi)核根用戶配置將樹的slot數(shù)定義為4或6,即每個結(jié)點有16或64個slot,如圖2所示,當(dāng)樹高為1時,64個slot對應(yīng)64個頁,當(dāng)樹高為2時,對應(yīng)64*64個頁。


圖2 高為1和2、slot數(shù)為64的radix樹


Linux內(nèi)核radix樹的slot數(shù)定義如下(在lib/radix-tree.c中):


#ifdef __KERNEL__
/*值為6時,表示每個結(jié)點有2^6=64個slot,值為4時,表示有2^4=16個slot*/
#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
#else
#define RADIX_TREE_MAP_SHIFT 3 /* 用于更有強度的測試 */
#endif
/*表示1個葉子結(jié)點可映射的頁數(shù),如:1<<6=64,表示可映射64個slot映射64頁*/
#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1)
/*定義slot數(shù)占用的long類型長度個數(shù),每個slot用位圖1位進行標記,如:64個slot時,值為2*/
#define RADIX_TREE_TAG_LONGS \
((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG)


(3)結(jié)點結(jié)構(gòu)


樹的根結(jié)點結(jié)構(gòu)radix_tree_root列出如下(在include/linux/radix-tree.h中):
#define RADIX_TREE_MAX_TAGS 2 /*每個slot需要的最大標簽位數(shù)*/

/*根結(jié)點的標簽存放在gfp_mask中,通過__GFP_BITS_SHIFT移位得到 */
struct radix_tree_root {
    unsigned int height; /* 從葉子向上計算的樹高度 */
    gfp_t gfp_mask;
    /*間接指針指向結(jié)點而非數(shù)據(jù)條目,通過設(shè)置root->rnode的低位表示是否是間指針*/
    struct radix_tree_node *rnode;
#ifdef CONFIG_RADIX_TREE_CONCURRENT
    struct radix_tree_context *context;
    struct task_struct *owner;
#endif
};


結(jié)構(gòu)radix_tree_context列出如下:
struct radix_tree_context {
    struct radix_tree_root *root;
#ifdef CONFIG_RADIX_TREE_CONCURRENT
    spinlock_t *locked;
#endif
}


樹的結(jié)點結(jié)構(gòu)radix_tree_node列出如下(在lib/radix-tree.c中):

struct radix_tree_node {
    unsigned int height; /* 從葉子向上計算的樹高度 */
    unsigned int count; /*非葉子結(jié)點含有一個count域,表示出現(xiàn)在該結(jié)點的孩子的數(shù)量*/

    struct rcu_head rcu_head;
    void *slots[RADIX_TREE_MAP_SIZE]; /*結(jié)點的slot指針*/
   /* 結(jié)點標簽數(shù)組=每個slot需要的最大標簽位數(shù)*slot數(shù)所需的long類型變量數(shù) */
   unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};


Linux內(nèi)核radix樹高度是可變的,它依賴于存儲的索引鍵值。radix樹查詢必須知道樹高度,以便知道結(jié)點的slot是指向葉子結(jié)點的指針還是結(jié)點的其他層。Linux在結(jié)點結(jié)構(gòu)中有成員height存入樹高度信息。
Linux radix樹的獨特特征是標簽擴展。圖2顯示了有2個標簽,分別表示打開和關(guān)閉。這些標簽是結(jié)點結(jié)構(gòu)的成員tags,該標簽用一個位圖實現(xiàn),在加鎖的情況下設(shè)置和清除。每個標簽基本上是一個在radix樹頂層上的位圖序號。標簽與群查詢一起用來在給定的范圍內(nèi)查找給定標簽集的頁。
標簽在每結(jié)點位圖中維護,在每個給定的級別上,可以判定下一個級別是否至少有一個標簽集。




圖2 帶有2位圖標簽指示的8位radix樹


在結(jié)點結(jié)構(gòu)radix_tree_node中,tags域是一個二維數(shù)組,每個slot用2位標識,這是一個典型的用空間換時間的應(yīng)用。tags域用于記錄該結(jié)點下面的子結(jié)點有沒有相應(yīng)的標志位。目前RADIX_TREE_MAX_TAGS為2,表示只記錄兩個標志,其中tags[0]為PAGE_CACHE_DIRTY,tags[1]為PAGE_CACHE_WRITEBACK。如果當(dāng)前節(jié)點的tags[0]值為1,那么它的子樹節(jié)點就存在PAGE_CACHE_DIRTY節(jié)點,否則這個子樹分枝就不存在著這樣的節(jié)點,就不必再查找這個子樹了。比如在查找PG_dirty的頁面時,就不需要遍歷整個樹,而可以跳過那些tags[0]為0值的子樹,這樣就提高了查找效率。
“加標簽查詢”是返回有指定標簽集radix樹條目的查詢,可以查詢設(shè)置了標簽的條目。加標簽查詢可以并行執(zhí)行,但它們需要通過設(shè)置或清除標簽從操作上互斥。


(4) 并行操作的優(yōu)化


Linux radix樹并行操作包括并行查詢和并行修改,其中,并行修改在標準內(nèi)核中未完沒有實現(xiàn),需要通過打補丁獲得該功能。并行操作說明如下:


  • RCU并發(fā)查詢


通過使用RCU,RCU Radix樹可以進行完全并發(fā)的查詢操作。RCU從根本上要求原子操作地移動指針從數(shù)據(jù)結(jié)構(gòu)的一個版本到新的版本,保持舊版本直到系統(tǒng)經(jīng)過靜止狀態(tài)。在靜止狀態(tài)點,舊版本數(shù)據(jù)結(jié)構(gòu)已沒有用戶,因此可以被安全釋放。
RCU radix樹的修改操作之間還需要串行化,但是查詢不再需要與修改操作串行化。


  • 并發(fā)修改


RCU可使RCU radix樹查詢完全并行化,但修改操作成了“瓶頸”。這可通過將全樹的鎖破碎成較小的鎖進行改善,再明顯的方法是對結(jié)點進行加鎖而非對整個樹加鎖。
radix樹修改操作可分為單向和雙向操作。單向操作僅執(zhí)行從根節(jié)點和葉子結(jié)點的單方向指針移動,它包括插入、更新和設(shè)置標簽操作。雙向操作較復(fù)雜,它需要在指針移到葉子后又回移,它包括刪除和清除標簽操作。
梯級加鎖(Ladder Locking)和鎖耦合(Lock-Coupling)技術(shù)常用于數(shù)據(jù)庫方面,允許單向遍歷結(jié)點加鎖的樹(雙向可能產(chǎn)生死鎖)。如果所有的修改者從樹頂?shù)綐涞走M行修改,并且修改的結(jié)點持有鎖,那么,向下遍歷時對孩子加鎖,在孩子被鎖住時再釋放該結(jié)點鎖。在這種情況下并發(fā)操作是可能的,因為只要根結(jié)點解鎖,另一個操作就可以自上向下進行。如果兩操作的路徑?jīng)]有相同操作結(jié)點,后一個操作可能在前一個操作完成之前完成。最壞的情況是流水線操作,但這還是比串行化操作好很多。


雙向操作包括刪除和清除標簽操作,分別說明如下:


1)清除標簽


在radix樹中清除一個標簽包括向下遍歷樹、查找定位條目和清除條目標簽的操作。只要孩子結(jié)點沒有打標簽的條目,就可以向上遍歷結(jié)點清除標簽。結(jié)束條件是:如果遍歷遇到一個結(jié)點,在清除一個標簽后,它還有一個或多個條目帶有標簽集,就可以結(jié)束向上遍歷。為了與向下遍歷期間有同樣的結(jié)束點,將終止條件改為:向上遍歷將在有比清除標簽數(shù)更多標簽的結(jié)點處結(jié)束。這樣,不論何時遇到這樣的結(jié)點,將作為上遍歷樹的結(jié)束點。


2)刪除元素


刪除元素在刪除無用結(jié)點時還需要刪除該條目的所有標簽。它的終止條件需要滿足這兩個方面。向上回退遍歷樹時需要滿足下面的條件:當(dāng)遇到一個非空結(jié)點且沒有無用的標簽時應(yīng)終止向上回退遍歷樹。
在向下遍歷樹時鑒別此點的條件是:當(dāng)遇到有超過2個孩子的結(jié)點、并且每個標簽來說結(jié)點有多于一個標簽條目被清除時,結(jié)束向上遍歷。該條件用來鑒別向上回退遍歷的終止點。


(5)radix樹API說明


  • 聲明和初始化radix樹

聲明和初始化radix樹的方法列出如下:


#include <linux/radix-tree.h>
/* gfp_mask表示如何執(zhí)行內(nèi)存分配,如果操作(如:插入)以原子性上下文中執(zhí)行,其值為GFP_ATOMIC*/
RADIX_TREE(name, gfp_mask); /* 聲明和初始化名為name的樹*/

struct radix_tree_root my_tree;
INIT_RADIX_TREE(my_tree, gfp_mask);


  • 插入條目


插入條目的函數(shù)定義列出如下:

int radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)


函數(shù)radix_tree_insert插入條目item到樹root中,如果插入條目中內(nèi)存分配錯誤,將返回錯誤-ENOMEM。該函數(shù)不能覆蓋寫正存在的條目。如果索引鍵值index已存在于樹中,返回錯誤-EEXIST。插入操作成功是,返回0。
對于插入條目操作失敗將引起嚴重問題的場合,下面的一對函數(shù)可避免插入操作失?。?

int radix_tree_preload(gfp_t gfp_mask);
void radix_tree_preload_end(void);


函數(shù)radix_tree_preload嘗試用給定的gfp_mask分配足夠的內(nèi)存,保證下一個插入操作不會失敗。在調(diào)用插入操作函數(shù)之前調(diào)用此函數(shù),分配的結(jié)構(gòu)將存放在每CPU變量中。函數(shù)radix_tree_preload操作成功后,將完畢內(nèi)核搶占。因此,在插入操作完成之后,用戶應(yīng)調(diào)用函數(shù)radix_tree_preload_end打開內(nèi)核搶占。


  • 刪除條目


刪除條目的函數(shù)定義列出如下:

void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)


函數(shù)radix_tree_delete刪除與索引鍵值index相關(guān)的條目,如果刪除條目在樹中,返回該條目的指針,否則返回NULL。


  • 查詢操作


用于查詢操作的函數(shù)定義列出如下:

/*在樹中查找指定鍵值的條目,查找成功,返回該條目的指針,否則,返回NULL*/
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index);
/*返回指向slot的指針,該slot含有指向查找到條目的指針*/
void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index);
/*查詢返回max_items條目在results中。查詢時鍵值索引從first_index開始*/
radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);


  • 標簽操作


與標簽操作相關(guān)的函數(shù)說明列出如下:

/*將鍵值index對應(yīng)的條目設(shè)置標簽tag,返回值為設(shè)置標簽的條目*/
void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*從鍵值index對應(yīng)的條目清除標簽tag,返回值為清除標簽的條目*/
void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*檢查鍵值index對應(yīng)的條目是否為標簽tag,如果鍵值不存在,返回0,如果鍵值存在,但標簽未設(shè)置,返回-1;如果鍵值存在,且標簽已設(shè)置,返回1*/
int radix_tree_tag_get(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*從first_index起查詢樹root中標簽值為tag的條目,在results中返回*/
unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag);
/*如果樹root中有任何條目使用tag標簽,返回鍵值*/
int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag);


(6)并行操作使用radix樹API的方法


  • 查詢獲取slot操作


查詢操作支持RCU無阻塞并行讀操作,因此,需要遵循RCU的用法加RCU讀鎖,還需要將rcu_dereference()用于獲得的slot,在寫(或更新)操作時,需要給新的slot使用rcu_assign_pointer()。查詢操作的使用方法列出如下:

struct page **slot, *page;
rcu_read_lock();
slot = radix_tree_lookup_slot(&mapping->page_tree, index);
page = rcu_dereference(*slot);
rcu_read_unlock();


  • 查詢修改slot操作


Linux內(nèi)核的radix樹需要打補丁才支持并發(fā)修改。查詢僅有一個全局狀態(tài):RCU靜止狀態(tài),并發(fā)修改需要跟蹤持有什么鎖。鎖狀態(tài)對于操作來說必須是外部的,因此,我們需要實例化一個本地上下文跟蹤這些鎖。查詢修改slot的方法列出如下:

struct page **slot;
DEFINE_RADIX_TREE_CONTEXT(ctx,&mapping->page_tree);
radix_tree_lock(&ctx); /*鎖住了根結(jié)點*/
/* ctx.tree代替&mapping->page_tree作為根,可以傳遞上下文
slot = radix_tree_lookup_slot(tx.tree, index);
rcu_assign_pointer(*slot, new_page);
radix_tree_unlock(&ctx);


radix樹API函數(shù)radix_tree_lookup_slot含有鎖從樹頂向下移動機制,鎖移動的代碼部分列出如下:

void **radix_tree_lookup_slot(struct radix_tree *root, unsigned long index)
{
    ...
    RADIX_TREE_CONTEXT(context, root); /*提供上下文和實際的root指針*、
    ...
    do {
        ...
        /* 從樹頂向下移動鎖*/
        radix_ladder_lock(context, node);
        ...
    } while (height > 0);
    ...
}


(7)radix樹API的實現(xiàn)


樹的操作通常包括查找、插入、刪除和樹調(diào)整,下面分別說明radix樹這些操作的實現(xiàn)。


  • 查找單個條目


radix樹通過索引鍵值進行查詢,如圖1所示,它按路徑組成索引鍵值,圖中3級結(jié)點對應(yīng)3段6位表示的索引值,圖中key對應(yīng)的葉子slot的索引鍵值為“2,63,1”。通過葉子slot的指針slot[1]就可得到所存儲的數(shù)據(jù)item。因此,查詢迭代時,需要將索引鍵值“2,63,1”移去高2層“2,63”,得到葉子slot的指針索引鍵值“1”。


圖1 一個3級結(jié)點的radix樹及其鍵值表示


函數(shù)radix_tree_lookup執(zhí)行查找操作,查找方法是:從葉子到樹頂,通過數(shù)組索引鍵值值查看數(shù)組元素的方法,一層層地查找slot。其列出如下(在lib/radix-tree.c中):

void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
    unsigned int height, shift;
    struct radix_tree_node *node, **slot;

    node = rcu_dereference(root->rnode); /*獲取根結(jié)點*/
    if (node == NULL)
        return NULL;


    /*間接指針指向結(jié)點而非數(shù)據(jù)條目,通過設(shè)置root->rnode的低位表示是否是間指針。對于間接指針來說,樹高度值root->height大于0,但是RCU查找需要測試間接指針,因為root->height 值不可靠。這種問題僅的RCU下需要考慮*/
    if (!radix_tree_is_indirect_ptr(node)) { /*非間接指針,說明只有根結(jié)點*/
        if (index > 0)
            return NULL;
        return node;
    }


    /*獲取真正結(jié)點指針,因為根結(jié)點指針的第0位設(shè)置為1表示為間接指針。當(dāng)使用結(jié)點指針時,必須將第0位設(shè)置為0,因為地址以字對齊*/
    node = radix_tree_indirect_to_ptr(node);

    height = node->height;
    if (index > radix_tree_maxindex(height)) /*索引鍵值不能超過最大索引值*/
        return NULL;


    /*每層索引偏移值為RADIX_TREE_MAP_SHIFT,葉子索引值偏移基數(shù)為(樹高-1)*每層索引偏移值*/
    shift = (height-1) * RADIX_TREE_MAP_SHIFT;

    do { /*從葉子到樹頂,通過樹路徑組成的索引查找指定索引鍵值的slot*/
        slot = (struct radix_tree_node **)(node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); /*如:slots +1*/
        node = rcu_dereference(*slot);
        if (node == NULL)
            return NULL;

        shift -= RADIX_TREE_MAP_SHIFT; /*向上移一層,再迭代查找*/
        height--;
    } while (height > 0);

   return node;
}


  • 查找多個條目


函數(shù)radix_tree_gang_lookup執(zhí)行多個索引鍵值的查找,其列出如下:

unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items)
{
    unsigned long max_index;
    struct radix_tree_node *node;
    unsigned long cur_index = first_index;
    unsigned int ret;

    node = rcu_dereference(root->rnode);
    if (!node)
        return 0;

    if (!radix_tree_is_indirect_ptr(node)) { /*如果為非間接指針,表示只有根節(jié)點*/
        if (first_index > 0)
            return 0;
        results[0] = node;
        return 1;
    }
    node = radix_tree_indirect_to_ptr(node); /*清除用于間接指針標識的第0位*/

    max_index = radix_tree_maxindex(node->height); /*獲取樹的最大索引鍵值*/

    ret = 0;
    while (ret < max_items) { /* max_items為查找的最大條目數(shù)*/
        unsigned int nr_found;
        unsigned long next_index; /* 下一個搜索的索引鍵值*/

        if (cur_index > max_index) /*已查詢完所需查詢的索引鍵值*/
            break;
        nr_found = __lookup(node, results + ret, cur_index,
        max_items - ret, &next_index);
        ret += nr_found;
        if (next_index == 0)
            break;
        cur_index = next_index;
    }

    return ret;
}


static unsigned int
__lookup(struct radix_tree_node *slot, void **results, unsigned long index, unsigned int max_items, unsigned long *next_index)
{
    unsigned int nr_found = 0;
    unsigned int shift, height;
    unsigned long i;

    height = slot->height;
    if (height == 0)
        goto out;
    /*所有葉子slot的索引鍵值基數(shù)偏移*/
    shift = (height-1) * RADIX_TREE_MAP_SHIFT;

    /*從底層向樹頂層,
    for ( ; height > 1; height--) { /*從葉子向樹頂查找*/
        i = (index >> shift) & RADIX_TREE_MAP_MASK;
        for (;;) { /*遍歷每一層的各個路徑,由樹頂?shù)疆?dāng)前層一條路徑組成索引鍵值*/
            /*如果slot不為空,那么它掛有子結(jié)點,跳出本循環(huán),進入子結(jié)點層進行本循環(huán)*/
            if (slot->slots[i] != NULL)
                break;
           /*如果slot為空,就跳過slot對應(yīng)的所有索引鍵值*/
           /*清除索引號低位.將索引號與該層slot的起始索引號對齊*/
           index &= ~((1UL << shift) - 1);
           /*跳過一個slot的索引鍵值數(shù)*/
          index += 1UL << shift;
          if (index == 0)
              goto out; /* 32-bit wraparound */
          i++; /*找到多個slot*/
          if (i == RADIX_TREE_MAP_SIZE)
              goto out;
       }

       shift -= RADIX_TREE_MAP_SHIFT; /*向上移一層,基數(shù)偏移減少*/
       slot = rcu_dereference(slot->slots[i]);
       if (slot == NULL)
           goto out;

    }

    /* 返回找到的多個slot*/
    for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
        struct radix_tree_node *node;
        index++;
        node = slot->slots[i];
        if (node) {
            results[nr_found++] = rcu_dereference(node);
           if (nr_found == max_items)
               goto out;
        }
    }
    out:
    *next_index = index;
    return nr_found;
}


  • 插入條目


函數(shù)radix_tree_insert找到索引鍵值對應(yīng)的結(jié)點,將item加到該結(jié)點的slot指針上。其列出如下:

int radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)
{
    struct radix_tree_node *node = NULL, *slot;
    unsigned int height, shift;
    int offset;
    int error;

    BUG_ON(radix_tree_is_indirect_ptr(item));

    /* 如果樹的高度不夠,就擴展樹。函數(shù)radix_tree_maxindex計算樹高容納的最大索引*/
    if (index > radix_tree_maxindex(root->height)) {
        error = radix_tree_extend(root, index);
        if (error)
            return error;
    }

    slot = radix_tree_indirect_to_ptr(root->rnode);

    height = root->height;
    shift = (height-1) * RADIX_TREE_MAP_SHIFT; /*計算偏移基數(shù)*/

    offset = 0;
    while (height > 0) {
        if (slot == NULL) { /*如果slot為空,則需要加入孩子結(jié)點*/
            /* 分配slot */
            if (!(slot = radix_tree_node_alloc(root)))
                return -ENOMEM;
            slot->height = height;
            if (node) {/*添加slot*/
                rcu_assign_pointer(node->slots[offset], slot);
             node->count++;
         } else
             rcu_assign_pointer(root->rnode,radix_tree_ptr_to_indirect(slot));
        }

        /* 進入上一層*/
        offset = (index >> shift) & RADIX_TREE_MAP_MASK;
        node = slot;
        slot = node->slots[offset];
        shift -= RADIX_TREE_MAP_SHIFT;
        height--;
    }
    /*如果index對應(yīng)的slot已有映射頁面,返回-EEXIST*/
    if (slot != NULL)
        return -EEXIST;

    if (node) {
        node->count++; /*增加子結(jié)點的計數(shù)*/
        rcu_assign_pointer(node->slots[offset], item);
        BUG_ON(tag_get(node, 0, offset));
        BUG_ON(tag_get(node, 1, offset));
    } else { /*為頂層結(jié)點*/
        rcu_assign_pointer(root->rnode, item);
        BUG_ON(root_tag_get(root, 0));
        BUG_ON(root_tag_get(root, 1));
    }

    return 0;
}


  • 擴展樹的高度


如果當(dāng)前樹高度不足以存放index,就需要擴展樹,擴展方法是在舊樹頂上加新的根結(jié)點,并將原根結(jié)點的tag信息移到新根結(jié)點的第1個slot。函數(shù)radix_tree_extend 列出如下:
static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
{
    struct radix_tree_node *node;
    unsigned int height;
    int tag;

    /* 計算擴展的深度*/
    height = root->height + 1;
    /*如果index超過樹高所容納的最大索引值,樹高遞增*/
    while (index > radix_tree_maxindex(height)) 
        height++;

    /*到這里已計算好合適的高度height*/
    if (root->rnode == NULL) { /*只有根結(jié)點,設(shè)置好樹高度就可返回*/
        root->height = height;
        goto out;
    }

    /*將當(dāng)前樹擴展到高度height*/
    do {
        unsigned int newheight;
        if (!(node = radix_tree_node_alloc(root))) /*分配一個結(jié)點*/
            return -ENOMEM;

        /* 增加樹高,在樹頂點之上增加一個結(jié)點*/
        node->slots[0] = radix_tree_indirect_to_ptr(root->rnode);

        /*傳播tag信息到新根結(jié)點*/
        /*以前的根結(jié)點現(xiàn)成為新頂結(jié)點的第1個插槽。 如果以前的根結(jié)點打上了tag,就將新增結(jié)點的第1個插槽對應(yīng)的子節(jié)點打上相應(yīng)的tag*/
        for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
            if (root_tag_get(root, tag))
               tag_set(node, tag, 0);
        }

        newheight = root->height+1;
        node->height = newheight;
        node->count = 1;
        node = radix_tree_ptr_to_indirect(node);
        rcu_assign_pointer(root->rnode, node);
        root->height = newheight;
    } while (height > root->height);
out:
    return 0;
}


  • 刪除條目


函數(shù)radix_tree_delete刪除index對應(yīng)的條目,并返回刪除條目的地址。其列出如下:
void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
{
    /*數(shù)組path用于保存路徑上的結(jié)點及索引偏移值*/
    struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp = path;
    struct radix_tree_node *slot = NULL;
    struct radix_tree_node *to_free;
    unsigned int height, shift;
    int tag;
    int offset;

    height = root->height;
    if (index > radix_tree_maxindex(height)) /*index不能超過樹的最大索引值*/
        goto out;

    slot = root->rnode;
    if (height == 0) { /*只有根結(jié)點*/
        root_tag_clear_all(root);
        root->rnode = NULL;
        goto out;
    }
    slot = radix_tree_indirect_to_ptr(slot);

    shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
    pathp->node = NULL;
    /*獲取刪除條目的路徑*/
    /*將index從根到葉子的路徑所經(jīng)過的結(jié)點及相應(yīng)索引偏移值存放在數(shù)組pathp中*/
    do {
        if (slot == NULL)
            goto out;

        pathp++;
        offset = (index >> shift) & RADIX_TREE_MAP_MASK;
        pathp->offset = offset;
        pathp->node = slot;
        slot = slot->slots[offset];
        shift -= RADIX_TREE_MAP_SHIFT;
        height--;
    } while (height > 0);

    if (slot == NULL)
        goto out;

    /*清除與刪除條目相關(guān)的所有標簽*/
    for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
        if (tag_get(pathp->node, tag, pathp->offset))
            radix_tree_tag_clear(root, index, tag);
    }

    to_free = NULL;
    /*釋放不再需要的結(jié)點 */
    while (pathp->node) { /*刪除
        pathp->node->slots[pathp->offset] = NULL; /*清除slot*/
        pathp->node->count--; /*孩子數(shù)量減1*/
        /*調(diào)用RCU機制的函數(shù)call_rcu在最后一個引用結(jié)束時延遲釋放結(jié)點to_free */
        if (to_free)
            radix_tree_node_free(to_free);

        if (pathp->node->count) { /*還有孩子存在*/
           if (pathp->node == radix_tree_indirect_to_ptr(root->rnode)) /*為根結(jié)點的孩子*/
                radix_tree_shrink(root); /*樹縮小*/
           goto out;
        }

        /*釋放有0個slot的結(jié)點 */
        to_free = pathp->node;
        pathp--;

    }
    /*運行到這里,說明是根結(jié)點*/
    root_tag_clear_all(root);
    root->height = 0;
    root->rnode = NULL;
    if (to_free)
        radix_tree_node_free(to_free);

out:
    return slot;
}


  • 縮小樹的高度


函數(shù)radix_tree_shrink縮小樹的高度到最小。其列出如下:

static inline void radix_tree_shrink(struct radix_tree_root *root)
{
    /* 嘗試縮小樹的高度*/
    while (root->height > 0) {
        struct radix_tree_node *to_free = root->rnode;
        void *newptr;

        BUG_ON(!radix_tree_is_indirect_ptr(to_free));
        to_free = radix_tree_indirect_to_ptr(to_free);

        /*候選結(jié)點多于一個孩子或孩子不在最左邊slot,不能縮小樹的高度,跳出循環(huán)*/
        if (to_free->count != 1)
            break;
        if (!to_free->slots[0])
            break;

        /*不需要調(diào)用rcu_assign_pointer(),因為僅從樹的一部分到另一部分移動結(jié)點。如果釋放舊指針的引用to_free->slots[0]是安全的,那么釋放新指針的引用root->rnode也是安全的*/
       newptr = to_free->slots[0];
       if (root->height > 1)
           newptr = radix_tree_ptr_to_indirect(newptr);
       root->rnode = newptr;
       root->height--; 
       /* 僅釋放0結(jié)點*/
       tag_clear(to_free, 0, 0);
       tag_clear(to_free, 1, 0);
       to_free->slots[0] = NULL;
       to_free->count = 0;
       radix_tree_node_free(to_free);
    }
}

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
address_space與基樹
《算法導(dǎo)論》讀書筆記之第10章 基本數(shù)據(jù)結(jié)構(gòu)之二叉樹
C++二叉樹及其算法和應(yīng)用
STL 算法設(shè)計
第14講n
PAT甲級——A1064 Complete Binary Search Tree
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服