Linuxカーネルrbtree
カーネルモジュールを作成するときに非同期要求を記録するにはrbtreeが必要なので、ここに記録されているカーネルrbtreeの使用について調査および分析しました。この記事は主にカーネルドキュメントrbtree.txtに言及しています
rbtreeの紹介
赤黒木(rbtree)は、分類可能なキー/値データのペアを格納するために使用される自己平衡二分探索木です。基数木やハッシュテーブルとは異なります。
基数木は、スパース配列を効果的に格納するために使用されます(ノードの挿入、クエリ、および削除に長整数のインデックスを使用します)。インデックス値が大きすぎて、配列に直接格納できません。
ハッシュテーブルは、インデックスをハッシュしてクエリの範囲を狭めるために使用されますが、並べ替えられていないため、すばやく見つけることができません。
赤黒木はAVL木に似ていますが、最悪の場合、より高速なリアルタイムの挿入と削除のパフォーマンスを提供します。最大2回転を挿入し、最大3回転を削除して、ツリーのバランスを取り直します。ただし、AVLツリーと比較すると、クエリ時間はわずかに遅くなります(O(log n))。
Linuxカーネルは、次のようにrbtreeを広範囲に使用します。I/ Oスケジューリングアルゴリズムの期限とCFQはrbtreeを使用して要求を追跡します。高精度タイマーコードはrbtreeを使用してタイミングタスクを整理します。ext3ファイルシステムはrbtreeを使用してディレクトリエントリを追跡します。
rbtreeの使い方
カーネルrbtreeの実装は、ファイル「lib / rbtree.c」にあります。rbtreeを使用するには、ヘッダーファイルをインクルードする必要があります。
#include <linux/rbtree.h>
パフォーマンスを向上させるために、Linuxrbtreeは従来のツリーよりも少ない中間層を実装します。rbtreeのノード構造structrb_nodeは、ユーザーのデータ構造に直接埋め込まれます(従来の方法は、ポインターを介してデータ構造を指すことです)。rbtreeの挿入関数とクエリ関数は、Linux rbtreeが提供する基本関数を呼び出すことによってユーザー自身が実装します(従来の方法は、コールバック関数ポインターを提供することです)。また、btreeのロックもユーザーによって管理されます。
rbtreeを作成する
データデータ構造でstructrb_nodeを定義します。
struct mytype {
struct rb_node node;
char *keystring;
};
rbtreeノードを処理するときは、container_of()
マクロ定義からデータ構造ポインターを見つけてください。keystring
rbtreeのキーは文字列または整数として定義でき、ユーザー定義の並べ替えと検索に使用されます。
次に、rbtreeのルートノードを定義します。
struct rb_root mytree = RB_ROOT;
rbtreeを探す
ユーザーは、ルートから開始してキーの値を比較し、必要に応じて左側のノードまたは右側のノードを探すという方法で、自分でrbtree検索機能を実装します。
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リバランスを実行します。挿入位置が見つかると、その親ノードのリンクが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のキーが同じであることを確認する必要があります。同じでない場合、例外が発生します。
ノードコード例を削除します。
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;
}