Linux kernel rb-tree (2)

写了个简单的Demo,使用内核提供的接口创建了一个红黑树。

API

// include/linux/rbtree_augmented.h
#define RB_RED          0
#define RB_BLACK        1

// linux-5.4/include/linux/rbtree.h

struct rb_root {
        struct rb_node *rb_node;
};

struct rb_node {
        unsigned long  __rb_parent_color;
        struct rb_node *rb_right;
        struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));

#define RB_ROOT (struct rb_root) { NULL, }

#define rb_parent(r)   ((struct rb_node *)((r)->__rb_parent_color & ~3))
#define rb_color(rb)       __rb_color((rb)->__rb_parent_color)
#define __rb_color(pc)     ((pc) & 1)

#define rb_entry(ptr, type, member) container_of(ptr, type, member)

static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link);

void rb_insert_color(struct rb_node *node, struct rb_root *root);

Demo

code

/*
 * Linux kernel rb-tree test
 *
 * Tested on: Linux 5.4.0-58-generic #64-Ubuntu SMP Wed Dec 9 08:16:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
 *
 * */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/rbtree_augmented.h>

#define BUFF_SIZE 24

struct planet {
        char name[BUFF_SIZE];
        int mass;
        struct rb_node rb_node;
};

struct rb_root planet_rb_root = RB_ROOT;

void insert_planet( struct planet * planet);
void print_planet_tree(struct rb_node *n);
struct planet *create_planet(char * name, int mass);

// 创建一个节点(planet)
struct planet *create_planet(char * name, int mass){
        struct planet *new_plt;

        if(strlen(name) >= BUFF_SIZE){
                pr_info("[-] name too long\n");
                return NULL;
        }

        new_plt = vmalloc(sizeof(struct planet));

        strcpy(new_plt->name, name);

        new_plt->mass = mass;

        pr_info("%s created: %px\n", name, new_plt);

        return new_plt;
}

// 插入到红黑树(planet_rb_root)中
void insert_planet( struct planet * planet){
        struct rb_node **p = &planet_rb_root.rb_node;
        struct rb_node *parent = NULL;

        while(*p){
                struct planet *tmp_planet;
                parent = *p;
                tmp_planet = rb_entry(parent, struct planet, rb_node);
                if(planet->mass < tmp_planet->mass){
                        p = &(*p)->rb_left;
                }else{
                        p = &(*p)->rb_right;
                }
        }

        rb_link_node(&planet->rb_node, parent, p);
        rb_insert_color(&planet->rb_node, &planet_rb_root);
}


// 遍历红黑树并打印节点信息
void print_planet_tree(struct rb_node *n){
        struct planet *planet;
        char *p_buff =  vmalloc(4096);
        char *pos;

        if(!n){
                return;
        }

        planet = rb_entry(n, struct planet, rb_node);

        pos = p_buff;
        pos += sprintf(pos, "<%px> planet start ****************************************\n", planet);
        pos += sprintf(pos, "<%px> planet->name: %s(%px)\n", &planet->name, planet->name, planet->name);
        pos += sprintf(pos, "<%px> planet->mass: %d\n", &planet->mass, planet->mass);
        pos += sprintf(pos, "<%px> planet->rb_node: \n", &planet->rb_node);

        pos += sprintf(pos, "<%px> \tplanet->rb_node.__rb_parent_color %lx, rb_parent: %px, rb_color: %s\n",
                                &planet->rb_node.__rb_parent_color,
                                planet->rb_node.__rb_parent_color,
                                rb_parent(&planet->rb_node),
                                rb_color(&planet->rb_node)? "black": "red");

        pos += sprintf(pos, "<%px> \tplanet->rb_node.rb_right %px\n", &planet->rb_node.rb_right, planet->rb_node.rb_right);
        pos += sprintf(pos, "<%px> \tplanet->rb_node.rb_left %px\n", &planet->rb_node.rb_left, planet->rb_node.rb_left);
        pos += sprintf(pos, "<%px> planet end  *****************************************\n\n", planet+1);

        pr_info("%s", p_buff);

        vfree(p_buff);

        if(n->rb_left){
                print_planet_tree(n->rb_left);
        }
        if(n->rb_right){
                print_planet_tree(n->rb_right);
        }

        return;
}


void rbt(void){
        struct planet *earth, *mars, *saturn, *venus, *jupiter;

        earth = create_planet("Earth", 0);
        venus = create_planet("Venus", 1);
        saturn = create_planet("Saturn", 3);
        mars = create_planet("Mars", 4);
        jupiter = create_planet("Jupiter", 7);

        insert_planet(earth);
        insert_planet(venus);
        insert_planet(saturn);
        insert_planet(mars);
        insert_planet(jupiter);

        pr_info("\n<%px> planet_rb_root.rb_node: %px\n\n", &planet_rb_root.rb_node, planet_rb_root.rb_node);

        print_planet_tree(planet_rb_root.rb_node);
}


static int __init rbt_init(void)
{
    printk(KERN_INFO "Hello rbt\n");
    rbt();
    return 0;
}

static void __exit rbt_exit(void)
{
    printk(KERN_INFO "Goodbye rbt\n");
}


module_init(rbt_init);
module_exit(rbt_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("X++D");
MODULE_DESCRIPTION("Kernel xxx Module.");
MODULE_VERSION("0.1");

output

$ insmod rbt-t.ko
$ rmmod rbt-t.ko
$ dmesg

[1954676.388309] Earth created: ffff9d49c00b5000
[1954676.388311] Venus created: ffff9d49c0213000
[1954676.388314] Saturn created: ffff9d49c0247000
[1954676.388316] Mars created: ffff9d49c0277000
[1954676.388318] Jupiter created: ffff9d49c0279000
[1954676.388319]
                 <ffffffffc0ed5380> planet_rb_root.rb_node: ffff9d49c0213020

[1954676.388325] <ffff9d49c0213000> planet start ****************************************
                 <ffff9d49c0213000> planet->name: Venus(ffff9d49c0213000)
                 <ffff9d49c0213018> planet->mass: 1
                 <ffff9d49c0213020> planet->rb_node:
                 <ffff9d49c0213020>     planet->rb_node.__rb_parent_color 1, rb_parent: 0000000000000000, rb_color: black
                 <ffff9d49c0213028>     planet->rb_node.rb_right ffff9d49c0277020
                 <ffff9d49c0213030>     planet->rb_node.rb_left ffff9d49c00b5020
                 <ffff9d49c0213038> planet end  *****************************************

[1954676.388330] <ffff9d49c00b5000> planet start ****************************************
                 <ffff9d49c00b5000> planet->name: Earth(ffff9d49c00b5000)
                 <ffff9d49c00b5018> planet->mass: 0
                 <ffff9d49c00b5020> planet->rb_node:
                 <ffff9d49c00b5020>     planet->rb_node.__rb_parent_color ffff9d49c0213021, rb_parent: ffff9d49c0213020, rb_color: black
                 <ffff9d49c00b5028>     planet->rb_node.rb_right 0000000000000000
                 <ffff9d49c00b5030>     planet->rb_node.rb_left 0000000000000000
                 <ffff9d49c00b5038> planet end  *****************************************

[1954676.388335] <ffff9d49c0277000> planet start ****************************************
                 <ffff9d49c0277000> planet->name: Mars(ffff9d49c0277000)
                 <ffff9d49c0277018> planet->mass: 4
                 <ffff9d49c0277020> planet->rb_node:
                 <ffff9d49c0277020>     planet->rb_node.__rb_parent_color ffff9d49c0213021, rb_parent: ffff9d49c0213020, rb_color: black
                 <ffff9d49c0277028>     planet->rb_node.rb_right ffff9d49c0279020
                 <ffff9d49c0277030>     planet->rb_node.rb_left ffff9d49c0247020
                 <ffff9d49c0277038> planet end  *****************************************

[1954676.388340] <ffff9d49c0247000> planet start ****************************************
                 <ffff9d49c0247000> planet->name: Saturn(ffff9d49c0247000)
                 <ffff9d49c0247018> planet->mass: 3
                 <ffff9d49c0247020> planet->rb_node:
                 <ffff9d49c0247020>     planet->rb_node.__rb_parent_color ffff9d49c0277020, rb_parent: ffff9d49c0277020, rb_color: red
                 <ffff9d49c0247028>     planet->rb_node.rb_right 0000000000000000
                 <ffff9d49c0247030>     planet->rb_node.rb_left 0000000000000000
                 <ffff9d49c0247038> planet end  *****************************************

[1954676.388344] <ffff9d49c0279000> planet start ****************************************
                 <ffff9d49c0279000> planet->name: Jupiter(ffff9d49c0279000)
                 <ffff9d49c0279018> planet->mass: 7
                 <ffff9d49c0279020> planet->rb_node:
                 <ffff9d49c0279020>     planet->rb_node.__rb_parent_color ffff9d49c0277020, rb_parent: ffff9d49c0277020, rb_color: red
                 <ffff9d49c0279028>     planet->rb_node.rb_right 0000000000000000
                 <ffff9d49c0279030>     planet->rb_node.rb_left 0000000000000000
                 <ffff9d49c0279038> planet end  *****************************************

visualization

rbt-demo.png

画图工具:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

说明

比较迷惑的是__rb_parent_color这个点,它是unsigned long类型,同时包含了parent的指针和当前节点的color。

由于struct rb_nodelong长度对齐的,也就是说在64位机器上rb_node的内存起始地址都是8的倍数,所以地址的最低4个bit要么是0000要么是 1000,最低3bit是用不上的,而color只有0(red)和1(black) 刚好用parent地址的最低1个bit来保存就行了。

这样就能理解rb_parentrb_parent这两个宏了。

至于为什么rb_parent用的 __rb_parent_color & ~3而不是~7应该是为了兼容32位系统。但是为什么不是~1呢?

还有,为什么8字节对齐,内存起始地址就是8的倍数,🤔?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容