当前位置: 首页 > news >正文

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)){

...
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 股票分析系统设计方案大纲与细节
  • 基于对称点模式SDP(SDP, symmetrized dot pattern)轴承故障诊断方法(matlab和python实现开源)
  • 高并发内存池联调问题
  • 链表 OJ(一)
  • LIO-SAM编译ubuntu20.04 Noetic
  • Python地图可视化三大秘密武器
  • 数智驱动丨zAIoT 连续落地军工、科研院所和机械制造场景,推动数智化转型升级...
  • base SAS programming学习笔记10(combine data)
  • java synchronized关键字介绍
  • Three 圆柱坐标(Cylindrical)和 视锥体(Frustum)
  • 聊一聊中小企业如何开展持续交付
  • 【C++修行之道】string类练习题
  • 用HttpURLConnection复现http响应码405
  • 【记录】LaTex|LaTex 代码片段 Listings 添加带圆圈数字标号的箭头(又名 LaTex Tikz 库画箭头的简要介绍)
  • 【深度学习基础】MacOS PyCharm连接远程服务器
  • 【5+】跨webview多页面 触发事件(二)
  • Android框架之Volley
  • Hibernate【inverse和cascade属性】知识要点
  • iOS | NSProxy
  • Java面向对象及其三大特征
  • laravel with 查询列表限制条数
  • opencv python Meanshift 和 Camshift
  • SwizzleMethod 黑魔法
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 简单基于spring的redis配置(单机和集群模式)
  • 驱动程序原理
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 线上 python http server profile 实践
  • 译有关态射的一切
  • 数据可视化之下发图实践
  • ​学习一下,什么是预包装食品?​
  • #1014 : Trie树
  • #define 用法
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (多级缓存)缓存同步
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (一)Docker基本介绍
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转) Face-Resources
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)scrum常见工具列表
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NetCore 如何动态路由
  • .NET委托:一个关于C#的睡前故事
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • /proc/stat文件详解(翻译)
  • ??javascript里的变量问题
  • @RequestMapping 的作用是什么?
  • @软考考生,这份软考高分攻略你须知道