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

...

}

相关推荐
孤客网络科技工作室几秒前
VMware 虚拟机使用教程及 Kali Linux 安装指南
linux·虚拟机·kali linux
颇有几分姿色1 小时前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
AndyFrank1 小时前
mac crontab 不能使用问题简记
linux·运维·macos
筱源源2 小时前
Kafka-linux环境部署
linux·kafka
算法与编程之美2 小时前
文件的写入与读取
linux·运维·服务器
xianwu5432 小时前
反向代理模块
linux·开发语言·网络·git
Amelio_Ming3 小时前
Permissions 0755 for ‘/etc/ssh/ssh_host_rsa_key‘ are too open.问题解决
linux·运维·ssh
Ven%3 小时前
centos查看硬盘资源使用情况命令大全
linux·运维·centos
TeYiToKu4 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws4 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习