radix-tree(基数ツリー)
Rev.2を表示中。最新版はこちら。
radix-treeは基数ツリーというそうで、アドレス空間内のページを効率よく検索するためのツリーである。構造はext2/ext3のような間接ブロックの考え方で、その階層を動的に変化させるということだが、ノードの追加とかノードのtagメンバーの取り扱いなどよく考えられている。
#ifdef __KERNEL__
#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
#else
#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
#endif
#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
struct radix_tree_root {
unsigned int height;
gfp_t gfp_mask;
struct radix_tree_node *rnode;
};
struct radix_tree_node {
unsigned int height; /* Height from the bottom */
unsigned int count;
struct rcu_head rcu_head;
void *slots[RADIX_TREE_MAP_SIZE];
unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};
radix-treeはrootをトップとするradix_tree_node構造体をノードとして階層構造を作成する。heightはそのノードの階層レベルを表し、最下位位階層を1として、上位階層に従って1づつ大きくなっていく。rootのheight値が一番大きい。*slotsは実データないし下位ノードが設定される。1ノードあたりのスロット数はRADIX_TREE_MAP_SIZEで通常は64となる。ノードツリーはキーとなる32ビットインデックの下位から6ビットずつを割り当てる。(最上位は2ビット)従って最大階層は6階層となり、スロット数との関係は以下のようになる。ただし6階層ではインデックスは2ビットとなるため、すべてを使うわけでない。
階層 スロット数
0 1 1
1 64 64
2 64*64 4096
3 64*64*64 262144
4 64*64*64*64 16252928
5 64*64*64*64*64 1073741823
6 64*64*64*64*64*64 66571993088
下記は上記の内容を検証するためのものである。rootを静的変数radix_treeとして定義し、radix_tree_insert関数でmy_objectを登録し、このときの階層値を確認するものである。インデックスが0のときrootの*rnodeに繋がれている。そしてインデックスが1から63までは階層1、64から4095は階層2となっていくことが確認できる。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/radix-tree.h>
MODULE_LICENSE("GPL");
struct radix_tree_root radix_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN);
struct my_object {
char data[10];
};
struct my_object *myobj;
static int mod_init(void)
{
int error, i;
struct my_object *p;
error = radix_tree_preload(__GFP_NOWARN);
if (!error) {
for (i = 0; i < 4097; i++) {
myobj = kmalloc(sizeof(struct my_object), GFP_KERNEL);
sprintf(myobj->data, "%d", i);
error = radix_tree_insert(&radix_tree, i, myobj);
printk("insert:%d:%s\n", radix_tree.height, myobj->data);
}
radix_tree_preload_end();
if (!error) {
for (i = 0; i < 4097; i++) {
p = radix_tree_lookup(&radix_tree, i);
printk("look:%s\n", p->data);
}
}
}
return error;
}
static void mod_exit(void)
{
int i;
struct my_object *p;
for (i = 0; i < 4097; i++) {
p = radix_tree_lookup(&radix_tree, i);
kfree(p);
}
}
module_init(mod_init);
module_exit(mod_exit);
[root@localhost test]# dmesg insert:0:0 insert:1:1 insert:1:2 : : insert:1:62 insert:1:63 insert:2:64 insert:2:65 : : insert:2:4094 insert:2:4095 insert:3:4096 look:0 look:1 : : look:4095 look:4096
radix_tree_insert関数は引数のインデックスでrootから対応するノードを辿ってスロットは見つけるわけだが、見つからなければradix_tree_extend関数で新規ノードを割り当てる。このノードはroot配下に割り付ける。そして以下のノードのheightを1インクリメントする。そしてradix_tree_insert関数へ戻ると、目的とするデータのインデックスに応じた必要とする階層ノードを作成しながら、そのツリー下に該当するノードを作成する。
radix_tree_node構造体にtagsメンバーは、汚れているページ検索の効率化を図っている。汚れているページを有しているツリーノードのtagsメンバーにその旨の情報を設定することで、そうでないノードは検索する必要がなくなる。
補足
radix_tree_preload関数はカーネルプリエンプションを禁止し、64GBまでの登録処理を保障するものらしい。




