linux 内核 红黑树接口说明

红黑树(rbtree)在linux内核中使用非常广泛,cfs调度任务管理,vma管理等。本文不会涉及关于红黑树插入和删除时的各种case的详细描述,感兴趣的读者可以查阅其他资料。本文主要聚焦于linux内核中经典rbtree和augment-rbtree操作接口的说明。

1、基本概念

二叉树:每个结点最多2棵子树,无其它限制了。

二叉查找树(二叉排序树/二叉搜索树):首先它是二叉树,左子树上所有结点的值小于它根结点的值,右子树上所有结点的值大于它根结点的值(递归定义).

二叉平衡树:也称为平衡二叉树,它是"平衡二叉搜索树"的简称。首先它是"二叉搜索树",其次它是平衡的,即它的每一个结点的左子树的高度和右子树的高度差至多为1。

红黑树性质:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。除二叉查找树强制一般要求以外,对于任何有效的红黑树增加了如下的额外要求:

性质1. 节点是红色或黑色。

性质2. 根是黑色。

性质3. 所有叶子都是黑色(叶子是NULL节点)。

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

 * red-black trees properties:  http://en.wikipedia.org/wiki/Rbtree

 *

 *  1) A node is either red or black

 *  2) The root is black

 *  3) All leaves (NULL) are black

 *  4) Both children of every red node are black

 *  5) Every simple path from root to leaves contains the same number of black nodes.

 *

 *  4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two

 *  consecutive red nodes in a path and every red node is therefore followed by

 *  a black. So if B is the number of black nodes on every simple path (as per

 *  5), then the longest possible path due to 4 is 2B.

 *

 *  We shall indicate color with case, where black nodes are uppercase(大写字母) and red nodes will be lowercase(小写字母).

 *  Unknown color nodes shall be drawn as red within parentheses and have some accompanying text comment.

linux内核中的红黑树分为两类,一类是经典的红黑树,用于存放key/value键值对,另一类是增强型红黑树(VMA是内核中典型的augment-rbtree)。

增强型rbtree是一种在每个节点中存储了“一些”额外数据的rbtree,其中节点N的额外数据必须是根为N的子树中所有节点内容的函数。

这些数据可用于为rbtree增加一些新功能。增强rbtree是建立在基本rbtree基础设施之上的可选功能。

需要此特性的rbtree用户在插入和删除节点时必须使用用户提供的增强回调调用增强函数。

注意内核红黑树的实现将部分工作留给了用户来实现:用户需要编写自己的树搜索和插入函数调用所提供的rbtree函数,锁也留给rbtree代码的用户。

2、数据结构

/*linux内核中,rbtree作为通用数据结构类似链表是嵌入到用户数据结构内部,在用户数据结构中存放自己的数据*/
struct rb_node {/*父节点,由于struct rb_node是long对齐,所以其地址低3-0bit或7-0bit未使用,低2位被用来作为颜色标志使用*/unsigned long  __rb_parent_color;struct rb_node *rb_right; /*右子树*/struct rb_node *rb_left;  /*左子树*/
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */注意,struct rb_node为long字节对齐,其地址最少也是4字节对齐,所以其成员__rb_parent_color用于存放其parent的地址,同时低2bit可以存放自身的----颜色属性。/*根节点*/
struct rb_root {struct rb_node *rb_node;
};/*节点颜色,默认插入节点为红色*/
#define	RB_RED		0
#define	RB_BLACK		1/*父节点地址, &~3 去掉颜色标志位*/
#define rb_parent(r)   		((struct rb_node *)((r)->__rb_parent_color & ~3))#define RB_ROOT		(struct rb_root) { NULL, }
#define RB_ROOT_CACHED 	(struct rb_root_cached) { {NULL, }, NULL }#define __rb_parent(pc)    ((struct rb_node *)(pc & ~3))/*pc节点的颜色*/
#define __rb_color(pc)     ((pc) & 1)
#define __rb_is_black(pc)  __rb_color(pc)
#define __rb_is_red(pc)    (!__rb_color(pc))/*rb->__rb_parent_color的颜色*/
#define rb_color(rb)       __rb_color((rb)->__rb_parent_color)
#define rb_is_red(rb)      __rb_is_red((rb)->__rb_parent_color)
#define rb_is_black(rb)    __rb_is_black((rb)->__rb_parent_color)/*返回内嵌struct rb_node的数据结构*/
#define	rb_entry(ptr, type, member) container_of(ptr, type, member)#define RB_EMPTY_ROOT(root)  (READ_ONCE((root)->rb_node) == NULL)/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
#define RB_EMPTY_NODE(node)  \((node)->__rb_parent_color == (unsigned long)(node))/*注意,这里是赋值操作*/
#define RB_CLEAR_NODE(node)  \((node)->__rb_parent_color = (unsigned long)(node))

3、接口说明

3.1、rbtree插入红黑树节点

3.1.1、经典rbtree插入红黑树节点

在将数据插入rbtree之前,需要用户实现查找函数,查找插入节点应该插入到rbtree root中的位置,建立链接后,才能将其插入到root中;

系统无法知道用户数据存放规则,将节点存放到rbtree中的位置的查找工作交给用户来处理。

通过rb_link_node(...)接口设置node要被插入到parent下面,建立位置链接关系
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,struct rb_node **rb_link)
{/*设置node__rb_parent_color的值,颜色属性为红色*/node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL;*rb_link = node;
}在树中插入数据包括首先搜索插入新节点的位置,然后插入节点并重新平衡(“重新上色”)树。
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{__rb_insert(node, root, dummy_rotate);
}
节点插入的工作交给__rb_insert来处理。下面是__rb_insert函数原型:
static __always_inline void __rb_insert(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))其中augment_rotate函数指针传入旋转回调函数,经典红黑树中未使用,传入哑旋转回调函数dummy_rotate;经典红黑树只是存储节点之间的顺序关系,无其他"额外"信息,所以其struct rb_augment_callbacks 增强回调函数全部实现为空;
/** Non-augmented rbtree manipulation functions.(非增强红黑树操作功能函数)** We use dummy augmented callbacks here, and have the compiler optimize them* out of the rb_insert_color() and rb_erase() function definitions.*/static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {}
static inline void dummy_copy(struct rb_node *old, struct rb_node *new) {}
static inline void dummy_rotate(struct rb_node *old, struct rb_node *new) {}static const struct rb_augment_callbacks dummy_callbacks = {dummy_propagate, dummy_copy, dummy_rotate
};/** Please note - only struct rb_augment_callbacks and the prototypes for* rb_insert_augmented() and rb_erase_augmented() are intended to be public.* The rest are implementation details you are not expected to depend on.** See Documentation/rbtree.txt for documentation and samples.*/struct rb_augment_callbacks {void (*propagate)(struct rb_node *node, struct rb_node *stop);void (*copy)(struct rb_node *old, struct rb_node *new);void (*rotate)(struct rb_node *old, struct rb_node *new);
};
对于augment-rbtree(增强红黑树)rb_augment_callbacks的定义可以通过下面的宏来实现;
/*这个宏定义的内容比较长,定义了augment回调函数接口以及对应的struct rb_augment_callbacks rbname 结构体*/
#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield,	\rbtype, rbaugmented, rbcompute)		\
static inline void							\
rbname ## _propagate(struct rb_node *rb, struct rb_node *stop)		\
{									\while (rb != stop) {						\rbstruct *node = rb_entry(rb, rbstruct, rbfield);	\rbtype augmented = rbcompute(node);			\if (node->rbaugmented == augmented)			\break;						\node->rbaugmented = augmented;				\rb = rb_parent(&node->rbfield);				\}								\
}									\
static inline void							\
rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new)		\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\
}									\
static void								\
rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new)	\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\old->rbaugmented = rbcompute(old);				\
}									\
rbstatic const struct rb_augment_callbacks rbname = {			\rbname ## _propagate, rbname ## _copy, rbname ## _rotate	\
};

3.1.2、augment-rbtree(增强红黑树)插入红黑树节点

在插入时,用户必须更新通向插入节点的路径上的增强信息,然后像往常一样调用rb_link_node()和rb_augment_inserted(),而不是通常的rb_insert_color()调用。

如果rb_augment_inserts()重新平衡了rbtree,它将回调为用户提供的函数,以更新受影响子树上的增强信息。

/** Fixup the rbtree and update the augmented information when rebalancing.** On insertion, the user must update the augmented information on the path* leading to the inserted node, then call rb_link_node() as usual and* rb_augment_inserted() instead of the usual rb_insert_color() call.* If rb_augment_inserted() rebalances the rbtree, it will callback into* a user provided function to update the augmented information on the* affected subtrees.*/
static inline void rb_insert_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{__rb_insert_augmented(node, root, augment->rotate);
}/** Augmented rbtree manipulation functions.** This instantiates the same __always_inline functions as in the non-augmented* case, but this time with user-defined callbacks.*/void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
{__rb_insert(node, root, augment_rotate);
}

和经典红黑树插入节点操作一样,最后的后都是留给__rb_insert来处理的。区别在于需要提供augmet->rotate的实现。

3.2、rbtree删除红黑树节点

3.2.1、经典rbtree删除红黑树节点

void rb_erase(struct rb_node *node, struct rb_root *root)
{struct rb_node *rebalance;rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);if (rebalance)____rb_erase_color(rebalance, root, dummy_rotate);
}/* Fast replacement of a single node without remove/rebalance/add/rebalance */
void rb_replace_node(struct rb_node *victim, struct rb_node *new,struct rb_root *root)
{struct rb_node *parent = rb_parent(victim);/* Set the surrounding nodes to point to the replacement */__rb_change_child(victim, new, parent, root);if (victim->rb_left)rb_set_parent(victim->rb_left, new);if (victim->rb_right)rb_set_parent(victim->rb_right, new);/* Copy the pointers/colour from the victim to the replacement */*new = *victim;
}

3.2.2、augment-rbtree(增强红黑树)删除红黑树节点

当擦除节点时,用户必须调用rb_erase_augmented()而不是rb_erase()。Rb_erase_augmented()回调用户提供的函数来更新受影响子树上的增强信息。

static __always_inline void rb_erase_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);if (rebalance)__rb_erase_color(rebalance, root, augment->rotate);
}	

3.3、rbtree节点遍历

/*如果rbtree中的节点是按顺存放的话,rb_first返回最小值节点*/

struct rb_node *rb_first(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_left)n = n->rb_left;return n;
}/*如果rbtree中的节点是按顺存放的话,rb_last返回最大值节点*/
struct rb_node *rb_last(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_right)n = n->rb_right;return n;
}/*如果rbtree中的节点是按顺存放的话,rb_next返回值比node节点值大的节点*/
struct rb_node *rb_next(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a right-hand child, go down and then left as far* as we can.*/if (node->rb_right) { /*node右子树上的值都比node大*/node = node->rb_right;while (node->rb_left) /*一直寻找左子树*/node=node->rb_left;return (struct rb_node *)node;}/** No right-hand children. Everything down and left is smaller than us,* so any 'next' node must be in the general direction of our parent.* Go up the tree; any time the ancestor is a right-hand child of its* parent, keep going up. First time it's a left-hand child of its* parent, said parent is our 'next' node.*//*node无右子树且node的parent存在:1、如果node为parent的左节点,则返回parent(parent比node大);2、node为其parent的右节点(parent比node小),则继续递归往上找(如果一直为右节点,表明node是以当前parent为root的这棵子树上的最大值),直到找到node为parent的左节点时返回其parent(parent比左子树所以节点都大);*/while ((parent = rb_parent(node)) && node == parent->rb_right)node = parent;return parent; /*这里返回的是parent*/
}/*如果rbtree中的节点是按顺存放的话,rb_next返回值比node节点值小的节点*/
struct rb_node *rb_prev(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a left-hand child, go down and then right as far* as we can.*/if (node->rb_left) {  /*node左子树上的值都比node小*/node = node->rb_left; while (node->rb_right) /*一直找右子树*/node=node->rb_right;return (struct rb_node *)node;}/** No left-hand children. Go up till we find an ancestor which* is a right-hand child of its parent.*//*node无左子树且node的parent存在:1、如果node为parent的右节点,则返回parent(parent比node小);	2、node为其parent的左节点(parent比node大),则继续递归往上找,(如果一直为左节点,表明node是以当前parent为root的这棵子树上的最小值),直到找到node为parent的右节点时返回其parent(parent比右子树所以节点都小);*/while ((parent = rb_parent(node)) && node == parent->rb_left)node = parent;return parent; /*这里返回的是parent*/
}

上面四个宏可以用于遍历红黑树中的节点:

for (node = rb_first(&mytree); node; node = rb_next(node)){

...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/374480.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哪个牌子开放式耳机质量好?五款全网爆火款式盘点!

开放式耳机是目前最流行的一种无线蓝牙耳机,与TWS耳机一样,拥有小巧轻盈的耳机主体,也有便携的补能收纳充电仓,但不同的是,开放式耳机有更加舒适的佩戴体验。作为资深数码产品测评师,我最近测评了多款产品&…

基于前馈神经网络 FNN 实现股票单变量时间序列预测(PyTorch版)

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对…

原生小程序生成二维码并保存到本地

需求:我要在一个页面中生成一个二维码,并且这个二维码可以长按保存到本地或者发送给好友; 我这里是将生成的canvas二维码转换成图片,利用长按图片进行保存或转发 效果图: 第一步先下载对应的包: npm instal…

Docker部署gitlab私有仓库后查看root默认密码以及修改external_url路径和端口的方法

文章目录 1、docker部署最新版gitlab2、进入gitlab容器3、修改路径地址ip和端口4、检验效果 1、docker部署最新版gitlab #docker安装命令 docker run --detach \--name gitlab \--restart always \-p 1080:80 \-p 10443:443 \-p 1022:22 \-v /gitlab/config:/etc/gitlab \-v …

Apache中使用CGI

Apache24 使用Visual Studio 2022 // CGI2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <stdio.h> #include <stdlib.h>#include <stdio.h>void main() {//设置HTML语言printf("Content-type:text/html\n\n&q…

Redis基本命令源码解析-字符串命令

1. set 用于将kv设置到数据库中 2. mset 批量设置kv mset (msetnx) key1 value1 key2 value2 ... mset:msetCommand msetnx:msetnxCommand msetCommand和msetnxCommand都调用msetGenericCommand 2.1 msetGenericCommand 如果参数个数为偶数,则响应参数错误并返回 如果…

【游戏客户端】大话slg玩法架构(二)背景地图

【游戏客户端】大话slg玩法架构&#xff08;二&#xff09;背景地图 大家好&#xff0c;我是Lampard家杰~~ 今天我们继续给大家分享SLG玩法的实现架构&#xff0c;关于SLG玩法的介绍可以参考这篇上一篇文章&#xff1a;【游戏客户端】制作率土之滨Like玩法 PS&#xff1a;和之前…

hudi数据湖万字全方位教程+应用示例

1、时间轴&#xff08;TimeLine&#xff09; Hudi的核心是维护表上在不同的即时时间&#xff08;instants&#xff09;执行的所有操作的时间轴&#xff08;timeline&#xff09;&#xff0c;这有助于提供表的即时视图 一个instant由以下三个部分组成&#xff1a; 1&#xff09;…

视频号矩阵系统源码,实现AI自动生成文案和自动回复私信评论,支持多个短视频平台

在当今短视频蓬勃发展的时代&#xff0c;视频号矩阵系统源码成为了自媒体人争相探索的宝藏。这一强大的技术工具不仅能帮助我们高效管理多个短视频平台&#xff0c;更能通过AI智能生成文案和自动回复私信评论&#xff0c;为自媒体运营带来前所未有的便利与效率。 一、视频号矩…

layui-表单(输入框)

1.基本使用方法 先写一个表单元素块 form 加上layui-form 里面写行区块结构&#xff0c;如下&#xff1a; 2.输入框样式选项 input框 placeholder默认文本 autocomplete自动填充 lay-verify required必填 3.下拉菜单样式选项 默认选择第一项 select框 disable禁…

导员:你这么牛,那你来讲讲你项目的核心流程-判题模块吧

耗时一个月开发的OJ在线判题系统&#xff0c;文末有项目地址&#xff0c;目前还在更新代码~ 今天我们来开发OJ系统后端核心流程之一的判题模块 文章目录 判题机模块与代码沙箱的关系代码沙箱架构开发判题服务开发判题服务业务流程判断逻辑策略模式优化 小知识-Lombox Builder …

新品牌快速成长指南:揭秘品牌成功的黄金法则

打造一个新品牌是一个系统性工程&#xff0c;不是一两句话就能说清楚的。 作为一个13年的营销人&#xff0c;今天试图给大家以最简练和通俗的文字&#xff0c;详细讲讲打造一个全新的品牌都需要做些啥&#xff1f;码字不易&#xff0c;请多给点支持哦。 一、市场调研与定位&a…

Elasticsearch 开放推理 API 增加了对 Amazon Bedrock 的支持

作者&#xff1a;来自 Elastic Mark Hoy, Hemant Malik Elasticsearch 开放推理 API 增加了对托管在 Amazon Bedrock 上的模型生成嵌入的支持。 Elasticsearch 开放 infereence API 使开发人员能够创建推理端点并使用来自领先提供商的机器学习模型。从今天开始&#xff0c;托管…

超简单的通配证书签发工具,免费,无需安装任何插件到本地

常见的acme.sh 或者 lego等工具需要配置&#xff0c;安装不灵活&#xff0c;续签需要配置计划任务&#xff0c;签发单域名证书或者通配证书需要不同的指令和配置&#xff0c;繁琐&#xff0c;如果自己程序想要对接签发证书的api有的不支持&#xff0c;有的用起来繁琐。 最近发…

确保智慧校园安全,充分利用操作日志功能

智慧校园基础平台系统的操作日志功能是确保整个平台运行透明、安全及可追溯的核心组件。它自动且详尽地记录下系统内的每一次关键操作细节&#xff0c;涵盖操作的具体时间、执行操作的用户账号、涉及的数据对象&#xff08;例如学生信息更新、课程调度变动等&#xff09;、操作…

华为HCIP Datacom H12-821 卷34

1.单选题 防火墙默认已经创建了一些安全区域,以下哪一个安全区域不是防火墙上默认存在的? A、Trust B、DMZ C、Internet D、Local 正确答案&#xff1a; C 解析&#xff1a; 防火墙默认情况下为我们提供了三个安全区域&#xff0c;分别是 Trust、DMZ和Untrust 2.判断题 …

(图文详解)小程序AppID申请以及在Hbuilderx中运行

今天小编给大家带来了如何去申请APPID&#xff0c;如果你是小程序的开发者&#xff0c;就必须要这个id。 申请步骤 到小程序注册页面&#xff0c;注册一个小程序账号 微信公众平台 填完信息后提交注册 会在邮箱收到 链接激活账号 确认。邮箱打开链接后&#xff0c;会输入实…

设备管理中的数据结构

一、有哪些数据结构属于设备管理数据结构 1. 设备控制表&#xff08;DCT&#xff09; “Device Control Table”的首字母缩写 2. 控制器控制表&#xff08;COCT&#xff09; “Controller Of Control Table”的首字母缩写。 3. 通道控制表&#xff08;CHCT&#xff09; “…

guided-diffusion 相比于improved-diffusion的sample增加的cond_fn()

目录 1、cond_fn()函数代码2、softmax与log_softmax函数 1、cond_fn()函数代码 def cond_fn(x, t, yNone):assert y is not Nonewith th.enable_grad():x_in x.detach().requires_grad_(True)logits classifier(x_in, t)log_probs F.log_softmax(logits, dim-1)selected l…

Transformer特辑

https://github.com/LongxingTan/Machine-learning-interview 模型结构 基本单元&#xff1a;token_embedding positional encoding, encoder, token_embedding positional encoding, decoderencoder: (self-attention, skip-connect, ln), (ffn, skip-connect, ln)decoder:…