因編寫內核模塊時須要用到rbtree來記錄異步request,研究分析了一下kernel rbtree的使用方法,記錄於此。本文主要參考了內核文檔rbtree.txtnode
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的實如今文件"lib/rbtree.c",使用rbtree須要包含頭文件:數組
#include <linux/rbtree.h>
爲了提升性能,linux rbtree比傳統的tree實現了更少的中間層。rbtree的節點結構體struct rb_node直接嵌入到使用者的data structure(傳統的方法是經過指針指向了data structure)。rbtree的插入和查詢函數由使用者經過調用linux rbtree提供的基礎函數本身實現(傳統的方法是提供回調函數指針)。而且btree的鎖也由使用者本身管理。數據結構
在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的查找函數,經過以下方法:從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); }
以下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);