【Linux编程-数据结构】rbtree

 

 

Linux kernel rbtree

因编写内核模块时需要用到rbtree来记录异步request,研究分析了一下kernel rbtree的使用方法,记录于此。本文主要参考了内核文档rbtree.txt

rbtree简介

Red-black trees(rbtree)是一种自平衡的二叉搜索树,用于存储可分类的key/value数据对。它不同于radix trees或者hash tables。
radix trees用于有效存储稀疏数组(使用长整型索引进行节点的插入、查询和删除),其索引值太大无法用数组直接存储。
hash tables用于散列索引缩小查询的范围,但它没有做排序,因此不能快速的定位。

Red-black trees和AVL trees很相似,但是提供了最坏情况下更快的实时插入和删除性能。插入最多2次rotations、删除最多3次rotations即可完成tree的重平衡。不过相比AVL trees,其查询时间稍慢(O(log n))。

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节点。

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);

rbtree.h中删除#include 和#include 两行,添加#include

对于rb_node的声明删除掉最后的__attribute__((aligned(sizeof(long))))

最后在rbtree.h中添加如下一些宏定义:

#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#define container_of(ptr, type, member) ({            /
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
        (type *)( (char *)__mptr - offsetof(type,member) );})

 

在使用rbtree的时候,每个rbtree的元素都由使用者自己定义如:
struct my_item
{
  struct rb_node node;
  struct my_data_struct data;
};
其中的struct my_data_struct是自己的数据区域
struct my_data_struct
{
  long key;
  long value;
};

下面需要实现两个函数,第一个是一个search函数,形式如下:
struct my_item* _my_rbtree_search (struct rb_root* root, long key);
其中的root参数是根节点指针;key是关键字,rbtree根据它来排序以及查找。
该函数的实现如下:
struct my_item*
_my_rbtree_search (struct rb_root* root, long key)
{
  struct rb_node *node;
  struct my_item* item;
  node = root->rb_node;

  while (node)
    {
      item = rb_entry (node, struct my_item, node);
      if (item->data.key > key)
        node = node->rb_left;
      else if (item->data.key < key)
        node = node->rb_right;
      else
        {
          return item;            /* found it */
        }
    }
  return NULL;
}

下面还需要实现一个插入的函数:
void
_my_rbtree_insert (struct rb_root* root, struct my_item* item)
{
  struct rb_node **link, *parent;
  long value;
  struct my_item* p_item;
  link = &(root->rb_node);
  value = item->data.key;
  /* Go to the bottom of the tree */
  while (*link)
    {
      parent = *link;
      p_item = rb_entry(parent, struct my_item, node);
      if (p_item->data.key > value)
        link = &((*link)->rb_left);
      else if (p_item->data.key < value)
        link = &((*link)->rb_right);
      else
        return;
    }
  /* Put the new node there */
  rb_link_node(&(item->node), parent, link);
  rb_insert_color(&(item->node), root);
}

拥有了以上两个函数我们就可以实现search、insert、remove、modify、traverse等操作,下面给出大概的伪代码吧
struct my_data_struct search (struct rb_root* root, long key)
{
  struct my_data_struct data;
  struct my_item *item;
  memset (&data, 0, sizeof(struct my_data));
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    memcpy (&data, item->data, sizeof(struct my_data_struct));
  }
  return data;
}

int insert (struct rb_root* root, long key, long value)
{
  struct my_item  *item = (struct my_item*) malloc (sizeof(struct my_item));
  if (NULL == item)
  {
    return -1;
  }
  item->data.key = key;
  item->data.value = value;
  _my_rbtree_insert (root, item);
  return 0;
}

int remove (struct rb_root* root, long key)
{
  struct my_item* item;
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    rb_erase (&(item->node), root);
  }
  return 0;
}

int modify (struct rb_root* root, long key, long new_value)
{
  struct my_item* item;
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    item->data.value = new_value;
  }
  return 0;
}

int traverse (struct rb_root* root)
{
  struct rb_node* node;
  struct my_item* item;
  for (node = rb_first (root); node; node = rb_next (node))
  {
    item = rb_entry (node, struct my_item, node);
    printf ("key:%d/tvalue:%d/n", item->data.key, item->data.value);
  }
  return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值