因编写内核模块时须要用到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);