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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM