Linux kernel rbtree

Linux kernel rbtree

因編寫內核模塊時須要用到rbtree來記錄異步request,研究分析了一下kernel rbtree的使用方法,記錄於此。本文主要參考了內核文檔rbtree.txtnode

rbtree簡介

Red-black trees(rbtree)是一種自平衡的二叉搜索樹,用於存儲可分類的key/value數據對。它不一樣於radix trees或者hash tables。
radix trees用於有效存儲稀疏數組(使用長整型索引進行節點的插入、查詢和刪除),其索引值太大沒法用數組直接存儲。
hash tables用於散列索引縮小查詢的範圍,但它沒有作排序,所以不能快速的定位。linux

Red-black trees和AVL trees很類似,可是提供了最壞狀況下更快的實時插入和刪除性能。插入最多2次rotations、刪除最多3次rotations便可完成tree的重平衡。不過相比AVL trees,其查詢時間稍慢(O(log n))。git

Linux內核大量使用rbtree,如:I/O調度算法deadline和CFQ使用rbtree來跟蹤request;高精度定時器代碼使用rbtree來組織定時任務;ext3文件系統使用rbtree來跟蹤目錄entry;等等。算法

rbtree使用方法

內核rbtree的實如今文件"lib/rbtree.c",使用rbtree須要包含頭文件:數組

#include <linux/rbtree.h>

爲了提升性能,linux rbtree比傳統的tree實現了更少的中間層。rbtree的節點結構體struct rb_node直接嵌入到使用者的data structure(傳統的方法是經過指針指向了data structure)。rbtree的插入和查詢函數由使用者經過調用linux rbtree提供的基礎函數本身實現(傳統的方法是提供回調函數指針)。而且btree的鎖也由使用者本身管理。數據結構

建立rbtree

在data數據結構裏定義struct rb_node:異步

struct mytype {
    struct rb_node node;
    char *keystring;
};

當處理rbtree的節點時,經過container_of()宏定義找到data數據結構指針。keystring爲rbtree的key,能夠定義爲字符串或者整型,它將用於用戶自定義的排序和查找。函數

而後定義rbtree的root節點:性能

struct rb_root mytree = RB_ROOT;

查找rbtree

使用者本身實現rbtree的查找函數,經過以下方法:從root開始,比較key的值,而後根據須要查找left節點或者right節點。this

struct mytype *my_search(struct rb_root *root, char *string)
{
    struct rb_node *node = root->rb_node;
    while (node) {
        struct mytype *data = container_of(node, struct mytype, node);
        int result;
        result = strcmp(string, data->keystring);
        if (result < 0)
            node = node->rb_left;
        else if (result > 0)
            node = node->rb_right;
        else
            return data;
    }
    return NULL;
}

插入新節點

使用者本身實現rbtree的插入函數,先找到插入的位置(該位置爲NULL),而後插入新的節點並執行rbtree的重平衡。在查找到插入位置時,須要其parent節點的link用於rbtree的重平衡。

int my_insert(struct rb_root *root, struct mytype *data)
{
    struct rb_node **new = &(root->rb_node), *parent = NULL;
    /* Figure out where to put new node */
    while (*new) {
        struct mytype *this = container_of(*new, struct mytype, node);
        int result = strcmp(data->keystring, this->keystring);
        parent = *new;
        if (result < 0)
            new = &((*new)->rb_left);
        else if (result > 0)
            new = &((*new)->rb_right);
        else
            return FALSE;
    }
    /* Add new node and rebalance tree. */
    rb_link_node(&data->node, parent, new);
    rb_insert_color(&data->node, root);
    return TRUE;
}

刪除/覆蓋節點

經過以下函數刪除和覆蓋一個節點:

void rb_erase(struct rb_node *victim, struct rb_root *tree);
void rb_replace_node(struct rb_node *old, struct rb_node *new, struct rb_root *tree);

覆蓋一個節點並不會重平衡rbtree,所以必須保證new和old的key是同樣的,否者會致使異常。
刪除一個節點代碼示例:

struct mytype *data = mysearch(&mytree, "walrus");
if (data) {
    rb_erase(&data->node, &mytree);
    myfree(data);
}

按順序遍歷rbtree

以下4個函數用於順序遍歷rbtree:

struct rb_node *rb_first(struct rb_root *tree);
struct rb_node *rb_last(struct rb_root *tree);
struct rb_node *rb_next(struct rb_node *node);
struct rb_node *rb_prev(struct rb_node *node);

代碼示例:

struct rb_node *node;
for (node = rb_first(&mytree); node; node = rb_next(node))
    printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring);
相關文章
相關標籤/搜索