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

Linux kernel rbtree的更多相关文章

  1. Linux Kernel 排程機制介紹

    http://loda.hala01.com/2011/12/linux-kernel-%E6%8E%92%E7%A8%8B%E6%A9%9F%E5%88%B6%E4%BB%8B%E7%B4%B9/ ...

  2. Linux kernel make 常用选项介绍

    Linux kernel 编译方法大全记录 一.这是一个我自己写的自动make脚本: #!/bin/sh export ARCH=arm export CROSS_COMPILE=arm-linux- ...

  3. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  4. Linux Kernel 代码艺术——编译时断言

    本系列文章主要写我在阅读Linux内核过程中,关注的比较难以理解但又设计巧妙的代码片段(不关注OS的各个模块的设计思想,此部分我准备写在“深入理解Linux Kernel” 系列文章中),一来通过内核 ...

  5. linux kernel的中断子系统 softirq

    linux kernel的中断子系统之(八):softirq http://www.wowotech.net/irq_subsystem/soft-irq.html http://www.ibm.co ...

  6. karottc A Simple linux-virus Analysis、Linux Kernel <= 2.6.37 - Local Privilege Escalation、CVE-2010-4258、CVE-2010-3849、CVE-2010-3850

    catalog . 程序功能概述 . 感染文件 . 前置知识 . 获取ROOT权限: Linux Kernel <= - Local Privilege Escalation 1. 程序功能概述 ...

  7. linux kernel 字符设备详解

    有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备 ...

  8. linux kernel 杂谈

    首先介绍一下背景吧,工作三个星期了.复习了一波u-boot,跟了一下事件上报,搞了下平台设备,扣了一个内存检查代码. 想想生活是不是有点无聊.对啊,真的很无聊!!!! 无聊也没有办法啊,所以找点方法去 ...

  9. 与您共享Linux Kernel 4.8分支首个维护版本

    导读 Linux Kernel 4.8正式版于10月2日由Linus Torvalds发布,带来了包括AMDGPU OverDrive支持.NVIDIA Pascal支持.AMDGPU PowerPl ...

随机推荐

  1. SQLServer存储引擎——05.索引的结构和分类

    5. SQLServer存储引擎——索引的结构和分类 关系型数据库中以二维表来表达关系模型,表中的数据以页的形式存储在磁盘上,在SQL SERVER中,数据页是磁盘上8k的连续空间,那么,一个表的所有 ...

  2. upper_bound下确界

    //uppper_bound上确界找出首个大于某值的元素 #include<algorithm> #include<iostream> using namespace std; ...

  3. Python-OpenCV中的cv2.threshold

    目录 cv2.threshold()   主要记录Python-OpenCV中的cv2,threshold()方法:官方文档 cv2.threshold() def threshold(src, th ...

  4. Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre

    原文链接:https://blog.csdn.net/hq091117/article/details/79065199 https://blog.csdn.net/allen_tsang/artic ...

  5. 二分+最小生成树【bzoj2654】: tree

    2654: tree 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. 二分答案,然后跑最小生成树判断. 注意优先跑白色边. code: ...

  6. 给花_Q

  7. CF709B Checkpoints 模拟

    Vasya takes part in the orienteering competition. There are n checkpoints located along the line at ...

  8. java 中 静态泛型方法书写

    public class SpringBean { /** * */ public static <T> T getBean(Class<T> clazz,String nam ...

  9. P1147 连续自然数和(思维题)

    题目描述 对一个给定的自然数MM,求出所有的连续的自然数段,这些连续的自然数段中的全部数之和为MM. 例子:1998+1999+2000+2001+2002 = 100001998+1999+2000 ...

  10. tomcat 普通用户报错方案

    Tomcat Neither the JAVA_HOME nor the JRE_HOME environment variable is defined 一眼就能看出来是jdk的环境有问题,但是用了 ...