Linux内核之rbtree(红黑树)


   red-black tree(RB树)是一种平衡二叉树,它主要用于存储或者说索引可排序的键 值对数据。RB树(红黑树)与radix树和hash表都不同。radix树是一种比较适合用于 存储稀疏的数据集而且将用一个大整数进行插入,删除,查找的操作基础。而hash表 并不是以某种排序顺序进行存储,而且必须指定大小和hash函数。
     RB树与AVL树很相似,但是比AVL树有更好的插入和删除最坏情况的时间复杂度,以及 O(log n)的最坏查找时间复杂度。
     在Linux中有很多地方用到了RD树。anticipatory, deadline, 和CFQ I/O调度都使用 的是RB树进行请求跟踪,还有CD/DVD驱动的包管理也是如此。 高精度计时器(high-resolution timer)使用RB树组织定时请求。 EXT3文件系统也使用RB树来管理目录。 虚拟存储管理系统也是有RB树进行VMAs(Virtual Memory Areas)的管理。 当然还有文件描述符,密码钥匙,“等级令牌桶”调度的网络数据包都是用RB数据进 行组织和管理的。
    
    相关资料:
    Linux Weekly News article on red-black trees
    http://lwn.net/Articles/184495/
    Wikipedia entry on red-black trees

    http://en.wikipedia.org/wiki/Red-black_tree


    在Linux内核源代码中rb树的实现在lib/rbtree.c文件中,可以通过 #include "linux/rbtree.h"进行使用。
    
    在Linux内核中的RB树实现与传统的实现方式有些不同。它对针对内核对速度的需要做 了些优化。每一个rb_node节点是嵌入在用RB树进行组织的数据结构中,而不是用 rb_node指针进行数据结构的组织。

 在RB树里的数据节点包含了一个rb_node数据结构节点。如下:
 struct ceph_pool_weight
{
struct rb_node node;
u32 pool_id;
    u32 len;
    u32 weight[1024];
};

其中rb_node的结构为:

struct rb_node
{
        unsigned long  rb_parent_color;
#define        RB_RED                0
#define        RB_BLACK        1
        struct rb_node *rb_right;
        struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));


    可以通过container_of宏取得包含了rb_node的数据结构。也可以通过
   rb_entry(node,type,member)取得。
    其实是#define rb_entry(node,type,member) container_of(node,type,member)

    

内核中RB树的一些接口:

rb_first()//返回第一个节点,如:rb_node *pnode = rb_first(root);

rb_next()//返回下一个节点。如rb_next(pnode);

rb_entry()//返回该节点的对应的数据类型。#define rb_entry(node,type,member) container_of(node,type,member)

rb_erase(&pool_weight->node, root);//擦除一个节点

RB_EMPTY_ROOT(root);//判断红黑树是否为空


创建RBtree节点(结构体):

struct ceph_pool_weight
{
struct rb_node node;
u32 pool_id;
    u32 len;
    u32 weight[1024];
};

创建一个RBtree

struct ceph_pool_weight root;

root = RB_ROOT; //初始化红黑树

查找RBtree

static struct ceph_pool_weight *__lookup_new_weight_mapping(struct rb_root *root,u32 pool_id)
{
struct rb_node *n = root->rb_node;
struct ceph_pool_weight *pool_weight=NULL;
int c;
while (n)
    {
   pool_weight = rb_entry(n, struct ceph_pool_weight, node);
           if(pool_weight)
          {
            c = pool_id_cmp(pool_id, pool_weight->pool_id);
            if (c < 0)
            {
                n = n->rb_left;
            }
            else if (c > 0)
            {
                n = n->rb_right;
            }
            else
            {
                return pool_weight;
            }
          }
}
return NULL;
}

RBTree的插入:

static int __insert_pool_weight_mapping(struct ceph_pool_weight *pool_weight,
                           struct rb_root *root)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct ceph_pool_weight *pw = NULL;
int c;

while (*p) {
parent = *p;
pw = rb_entry(parent, struct ceph_pool_weight, node);
c = pool_id_cmp(pool_weight->pool_id, pw->pool_id);
if (c < 0)
p = &(*p)->rb_left;
else if (c > 0)
p = &(*p)->rb_right;
else
return -EEXIST;
}

rb_link_node(&pool_weight->node, parent, p);
rb_insert_color(&pool_weight->node, root);
return 0;
}

删除RBtree节点

static int __remove_new_weight_mapping(struct rb_root *root, u32 pool_id)
{

//首先查找到需要删除的节点
struct ceph_pool_weight *pool_weight = __lookup_new_weight_mapping(root, pool_id);

if (pool_weight)
    {
dout("__lookup_new_weight_mapping ;pool_id:%u. pool_weight=%p\n", pool_id, pool_weight);
rb_erase(&pool_weight->node, root); //将节点删除
kfree(pool_weight);
return 0;
    }
dout("__lookup_new_weight_mapping ;pool_id:%u. pool_weight=%p\n", pool_id, pool_weight);
return -ENOENT;
}

遍历或者查找复合条件的节点

    struct rb_node *pnode;
    pnode = rb_first(&map->osd_new_weight);
    struct ceph_pool_weight *cpw = rb_entry(pnode, struct ceph_pool_weight, node);
    while(cpw)
    {
        if (cpw->pool_id !=1) //如果不符合条件
        {
            pnode = rb_next(pnode);
            cpw = rb_entry(pnode, struct ceph_pool_weight, node);
            pr_err("anxiangwen ********** poolid=%d;  osd_len=%d weight=%d\n", cpw->pool_id, cpw->len,cpw->weight[0]);
        }
        else //符合条件
        {
            pr_err("anxiangwen ********** poolid=%d;  osd_len=%d weight=%d\n", cpw->pool_id, cpw->len,cpw->weight[0]);
            break;
        }
    }


  替换一个节点
    voidrb_replace_node(struct rb_node *old, struct rb_node *new,  struct rb_root *tree);
    来替换一个节点,但是替换完成后并不会对RB树做任何调整,所以如果新节点的值与
    被替换的值有所不同时,可能会出现问题。
    

你可能感兴趣的:(linux内核)