Linux内核红黑树2—移植笔记
一、学习笔记
1. rbtree 简介
rbtree,全称是 Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL 或 NULL)的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。此特性确保没有一条路径会比其他路径长出两倍,因而,红黑树是相对是接近平衡的二叉树。
对红黑树的所有操作都要保持红黑树的特性不变,红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。cfs_rq就是使用红黑树存储任务的。
2. 红黑树的基本操作
(1) 左旋
左旋示例图(以x为节点进行左旋):
z x / / \ --(左旋)--> x y z / y
对 x 进行左旋,意味着,将 “x的右孩子” 设为 “x的父亲节点”,即,将x变成了一个左节点(x成了为z的左孩子)。 因此,左旋中的 “左”,意味着 “被旋转的节点将变成一个左节点”。
(2) 右旋
右旋示例图(以x为节点进行右旋):
y x \ / \ --(右旋)--> x y z \ z
对 x 进行右旋,意味着,将 “x的左孩子” 设为 “x的父亲节点”,即,将x变成了一个右节点(x成了为y的右孩子),因此,右旋中的 “右”,意味着 “被旋转的节点将变成一个右节点”。
(3) 添加
将一个节点插入到红黑树中,首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过旋转和重新着色等方法来修正该树,使之重新成为一颗红黑树。
(4) 删除
将红黑树内的某一个节点删除。需要执行的操作依次是:首先,将红黑树当作一颗二叉查找树,将该节点从二叉查找树中删除;然后,通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
二、移植测试
1. 移植 linux/include/linux/rbtree.h
/* SPDX-License-Identifier: GPL-2.0-or-later */ /* linux/include/linux/rbtree.h */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H //#include <linux/kernel.h> //#include <linux/stddef.h> //#include <linux/rcupdate.h> /*------------------------- I add -------------------------*/ #include <stdlib.h> #define NULL ((void *)0) #define RB_RED 0 #define RB_BLACK 1 #define WRITE_ONCE(p, v) (p)=(v) #define unlikely(x) x #define rcu_assign_pointer(p, v) (p)=(v) typedef enum _bool { false = 0, true = 1, } bool; #define __rb_parent(pc) ((struct rb_node *)(pc & ~3)) #define __rb_color(pc) ((pc) & 1) #define __rb_is_black(pc) __rb_color(pc) #define __rb_is_red(pc) (!__rb_color(pc)) #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) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /*------------------ end ----------------------------*/ struct rb_node { 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_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) #define RB_ROOT (struct rb_root) { NULL, } #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)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(const struct rb_node *); extern struct rb_node *rb_prev(const struct rb_node *); extern struct rb_node *rb_first(const struct rb_root *); extern struct rb_node *rb_last(const struct rb_root *); /* Postorder iteration - always visit the parent after its children */ extern struct rb_node *rb_first_postorder(const struct rb_root *); extern struct rb_node *rb_next_postorder(const struct rb_node *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, struct rb_root *root); /* * 传参(&node->rb, parent, new) node是用户数据结构中的rb_node成员,rb_link是在红黑树上遍历出来的位置,要么为 * parent的rb_left,要么为rb_right */ static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link) { node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } static inline void rb_link_node_rcu(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link) { node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL; rcu_assign_pointer(*rb_link, node); } #define rb_entry_safe(ptr, type, member) \ ({ typeof(ptr) ____ptr = (ptr); \ ____ptr ? rb_entry(____ptr, type, member) : NULL; \ }) /** * rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of * given type allowing the backing memory of @pos to be invalidated * * @pos: the 'type *' to use as a loop cursor. * @n: another 'type *' to use as temporary storage * @root: 'rb_root *' of the rbtree. * @field: the name of the rb_node field within 'type'. * * rbtree_postorder_for_each_entry_safe() provides a similar guarantee as * list_for_each_entry_safe() and allows the iteration to continue independent * of changes to @pos by the body of the loop. * * Note, however, that it cannot handle other modifications that re-order the * rbtree it is iterating over. This includes calling rb_erase() on @pos, as * rb_erase() may rebalance the tree, causing us to miss some nodes. */ #define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \ for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \ pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \ typeof(*pos), field); 1; }); \ pos = n) /* * Leftmost-cached rbtrees. * * We do not cache the rightmost node based on footprint * size vs number of potential users that could benefit * from O(1) rb_last(). Just not worth it, users that want * this feature can always implement the logic explicitly. * Furthermore, users that want to cache both pointers may * find it a bit asymmetric, but that's ok. */ struct rb_root_cached { struct rb_root rb_root; struct rb_node *rb_leftmost; }; #define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL } /* Same as rb_first(), but O(1) */ #define rb_first_cached(root) (root)->rb_leftmost static inline void rb_insert_color_cached(struct rb_node *node, struct rb_root_cached *root, bool leftmost) { if (leftmost) root->rb_leftmost = node; rb_insert_color(node, &root->rb_root); } static inline void rb_erase_cached(struct rb_node *node, struct rb_root_cached *root) { if (root->rb_leftmost == node) root->rb_leftmost = rb_next(node); rb_erase(node, &root->rb_root); } static inline void rb_replace_node_cached(struct rb_node *victim, struct rb_node *new, struct rb_root_cached *root) { if (root->rb_leftmost == victim) root->rb_leftmost = new; rb_replace_node(victim, new, &root->rb_root); } #endif /* _LINUX_RBTREE_H */
2. 移植 linux/lib/rbtree.c
/* linux/lib/rbtree.c */ //#include <linux/rbtree_augmented.h> //#include <linux/export.h> /*------------------------------------- I add ----------------------------------------------*/ #include "rbtree.h" //linux/rbtree.h 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); }; static inline void rb_set_parent_color(struct rb_node *rb, struct rb_node *p, int color) { rb->__rb_parent_color = (unsigned long)p | color; } static inline void __rb_change_child(struct rb_node *old, struct rb_node *new, struct rb_node *parent, struct rb_root *root) { if (parent) { if (parent->rb_left == old) WRITE_ONCE(parent->rb_left, new); else WRITE_ONCE(parent->rb_right, new); } else WRITE_ONCE(root->rb_node, new); } static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->__rb_parent_color = rb_color(rb) | (unsigned long)p; } static inline struct rb_node *__rb_erase_augmented(struct rb_node *node, struct rb_root *root, const struct rb_augment_callbacks *augment) { struct rb_node *child = node->rb_right; struct rb_node *tmp = node->rb_left; struct rb_node *parent, *rebalance; unsigned long pc; if (!tmp) { /* * Case 1: node to erase has no more than 1 child (easy!) * * Note that if there is one child it must be red due to 5) * and node must be black due to 4). We adjust colors locally * so as to bypass __rb_erase_color() later on. */ pc = node->__rb_parent_color; parent = __rb_parent(pc); __rb_change_child(node, child, parent, root); if (child) { child->__rb_parent_color = pc; rebalance = NULL; } else rebalance = __rb_is_black(pc) ? parent : NULL; tmp = parent; } else if (!child) { /* Still case 1, but this time the child is node->rb_left */ tmp->__rb_parent_color = pc = node->__rb_parent_color; parent = __rb_parent(pc); __rb_change_child(node, tmp, parent, root); rebalance = NULL; tmp = parent; } else { struct rb_node *successor = child, *child2; tmp = child->rb_left; if (!tmp) { /* * Case 2: node's successor is its right child * * (n) (s) * / \ / \ * (x) (s) -> (x) (c) * \ * (c) */ parent = successor; child2 = successor->rb_right; augment->copy(node, successor); } else { /* * Case 3: node's successor is leftmost under * node's right child subtree * * (n) (s) * / \ / \ * (x) (y) -> (x) (y) * / / * (p) (p) * / / * (s) (c) * \ * (c) */ do { parent = successor; successor = tmp; tmp = tmp->rb_left; } while (tmp); child2 = successor->rb_right; WRITE_ONCE(parent->rb_left, child2); WRITE_ONCE(successor->rb_right, child); rb_set_parent(child, successor); augment->copy(node, successor); augment->propagate(parent, successor); } tmp = node->rb_left; WRITE_ONCE(successor->rb_left, tmp); rb_set_parent(tmp, successor); pc = node->__rb_parent_color; tmp = __rb_parent(pc); __rb_change_child(node, successor, tmp, root); if (child2) { successor->__rb_parent_color = pc; rb_set_parent_color(child2, parent, RB_BLACK); rebalance = NULL; } else { unsigned long pc2 = successor->__rb_parent_color; successor->__rb_parent_color = pc; rebalance = __rb_is_black(pc2) ? parent : NULL; } tmp = successor; } augment->propagate(tmp, NULL); return rebalance; } static inline void __rb_change_child_rcu(struct rb_node *old, struct rb_node *new, struct rb_node *parent, struct rb_root *root) { if (parent) { if (parent->rb_left == old) rcu_assign_pointer(parent->rb_left, new); else rcu_assign_pointer(parent->rb_right, new); } else rcu_assign_pointer(root->rb_node, new); } /*--------------------------------------- end ------------------------------------------------*/ static inline void rb_set_black(struct rb_node *rb) { rb->__rb_parent_color |= RB_BLACK; } static inline struct rb_node *rb_red_parent(struct rb_node *red) { return (struct rb_node *)red->__rb_parent_color; } /* * Helper function for rotations: * - old's parent and color get assigned to new * - old gets assigned new as a parent and 'color' as a color. */ static inline void __rb_rotate_set_parents(struct rb_node *old, struct rb_node *new, struct rb_root *root, int color) { struct rb_node *parent = rb_parent(old); new->__rb_parent_color = old->__rb_parent_color; rb_set_parent_color(old, new, color); __rb_change_child(old, new, parent, root); } 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)) { struct rb_node *parent = rb_red_parent(node), *gparent, *tmp; while (true) { /* * Loop invariant: node is red. */ if (unlikely(!parent)) { /* * The inserted node is root. Either this is the * first node, or we recursed at Case 1 below and * are no longer violating 4). */ rb_set_parent_color(node, NULL, RB_BLACK); break; } /* * If there is a black parent, we are done. * Otherwise, take some corrective action as, * per 4), we don't want a red root or two * consecutive red nodes. */ if(rb_is_black(parent)) break; gparent = rb_red_parent(parent); tmp = gparent->rb_right; if (parent != tmp) { /* parent == gparent->rb_left */ if (tmp && rb_is_red(tmp)) { /* * Case 1 - node's uncle is red (color flips). * * G g * / \ / \ * p u --> P U * / / * n n * * However, since g's parent might be red, and * 4) does not allow this, we need to recurse * at g. */ rb_set_parent_color(tmp, gparent, RB_BLACK); rb_set_parent_color(parent, gparent, RB_BLACK); node = gparent; parent = rb_parent(node); rb_set_parent_color(node, parent, RB_RED); continue; } tmp = parent->rb_right; if (node == tmp) { /* * Case 2 - node's uncle is black and node is * the parent's right child (left rotate at parent). * * G G * / \ / \ * p U --> n U * \ / * n p * * This still leaves us in violation of 4), the * continuation into Case 3 will fix that. */ tmp = node->rb_left; WRITE_ONCE(parent->rb_right, tmp); WRITE_ONCE(node->rb_left, parent); if (tmp) rb_set_parent_color(tmp, parent, RB_BLACK); rb_set_parent_color(parent, node, RB_RED); augment_rotate(parent, node); parent = node; tmp = node->rb_right; } /* * Case 3 - node's uncle is black and node is * the parent's left child (right rotate at gparent). * * G P * / \ / \ * p U --> n g * / \ * n U */ WRITE_ONCE(gparent->rb_left, tmp); /* == parent->rb_right */ WRITE_ONCE(parent->rb_right, gparent); if (tmp) rb_set_parent_color(tmp, gparent, RB_BLACK); __rb_rotate_set_parents(gparent, parent, root, RB_RED); augment_rotate(gparent, parent); break; } else { tmp = gparent->rb_left; if (tmp && rb_is_red(tmp)) { /* Case 1 - color flips */ rb_set_parent_color(tmp, gparent, RB_BLACK); rb_set_parent_color(parent, gparent, RB_BLACK); node = gparent; parent = rb_parent(node); rb_set_parent_color(node, parent, RB_RED); continue; } tmp = parent->rb_left; if (node == tmp) { /* Case 2 - right rotate at parent */ tmp = node->rb_right; WRITE_ONCE(parent->rb_left, tmp); WRITE_ONCE(node->rb_right, parent); if (tmp) rb_set_parent_color(tmp, parent, RB_BLACK); rb_set_parent_color(parent, node, RB_RED); augment_rotate(parent, node); parent = node; tmp = node->rb_left; } /* Case 3 - left rotate at gparent */ WRITE_ONCE(gparent->rb_right, tmp); /* == parent->rb_left */ WRITE_ONCE(parent->rb_left, gparent); if (tmp) rb_set_parent_color(tmp, gparent, RB_BLACK); __rb_rotate_set_parents(gparent, parent, root, RB_RED); augment_rotate(gparent, parent); break; } } } /* * Inline version for rb_erase() use - we want to be able to inline * and eliminate the dummy_rotate callback there */ static __always_inline void ____rb_erase_color(struct rb_node *parent, struct rb_root *root, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { struct rb_node *node = NULL, *sibling, *tmp1, *tmp2; while (true) { /* * Loop invariants: * - node is black (or NULL on first iteration) * - node is not the root (parent is not NULL) * - All leaf paths going through parent and node have a * black node count that is 1 lower than other leaf paths. */ sibling = parent->rb_right; if (node != sibling) { /* node == parent->rb_left */ if (rb_is_red(sibling)) { /* * Case 1 - left rotate at parent * * P S * / \ / \ * N s --> p Sr * / \ / \ * Sl Sr N Sl */ tmp1 = sibling->rb_left; WRITE_ONCE(parent->rb_right, tmp1); WRITE_ONCE(sibling->rb_left, parent); rb_set_parent_color(tmp1, parent, RB_BLACK); __rb_rotate_set_parents(parent, sibling, root, RB_RED); augment_rotate(parent, sibling); sibling = tmp1; } tmp1 = sibling->rb_right; if (!tmp1 || rb_is_black(tmp1)) { tmp2 = sibling->rb_left; if (!tmp2 || rb_is_black(tmp2)) { /* * Case 2 - sibling color flip * (p could be either color here) * * (p) (p) * / \ / \ * N S --> N s * / \ / \ * Sl Sr Sl Sr * * This leaves us violating 5) which * can be fixed by flipping p to black * if it was red, or by recursing at p. * p is red when coming from Case 1. */ rb_set_parent_color(sibling, parent, RB_RED); if (rb_is_red(parent)) rb_set_black(parent); else { node = parent; parent = rb_parent(node); if (parent) continue; } break; } /* * Case 3 - right rotate at sibling * (p could be either color here) * * (p) (p) * / \ / \ * N S --> N sl * / \ \ * sl Sr S * \ * Sr * * Note: p might be red, and then both * p and sl are red after rotation(which * breaks property 4). This is fixed in * Case 4 (in __rb_rotate_set_parents() * which set sl the color of p * and set p RB_BLACK) * * (p) (sl) * / \ / \ * N sl --> P S * \ / \ * S N Sr * \ * Sr */ tmp1 = tmp2->rb_right; WRITE_ONCE(sibling->rb_left, tmp1); WRITE_ONCE(tmp2->rb_right, sibling); WRITE_ONCE(parent->rb_right, tmp2); if (tmp1) rb_set_parent_color(tmp1, sibling, RB_BLACK); augment_rotate(sibling, tmp2); tmp1 = sibling; sibling = tmp2; } /* * Case 4 - left rotate at parent + color flips * (p and sl could be either color here. * After rotation, p becomes black, s acquires * p's color, and sl keeps its color) * * (p) (s) * / \ / \ * N S --> P Sr * / \ / \ * (sl) sr N (sl) */ tmp2 = sibling->rb_left; WRITE_ONCE(parent->rb_right, tmp2); WRITE_ONCE(sibling->rb_left, parent); rb_set_parent_color(tmp1, sibling, RB_BLACK); if (tmp2) rb_set_parent(tmp2, parent); __rb_rotate_set_parents(parent, sibling, root, RB_BLACK); augment_rotate(parent, sibling); break; } else { sibling = parent->rb_left; if (rb_is_red(sibling)) { /* Case 1 - right rotate at parent */ tmp1 = sibling->rb_right; WRITE_ONCE(parent->rb_left, tmp1); WRITE_ONCE(sibling->rb_right, parent); rb_set_parent_color(tmp1, parent, RB_BLACK); __rb_rotate_set_parents(parent, sibling, root, RB_RED); augment_rotate(parent, sibling); sibling = tmp1; } tmp1 = sibling->rb_left; if (!tmp1 || rb_is_black(tmp1)) { tmp2 = sibling->rb_right; if (!tmp2 || rb_is_black(tmp2)) { /* Case 2 - sibling color flip */ rb_set_parent_color(sibling, parent, RB_RED); if (rb_is_red(parent)) rb_set_black(parent); else { node = parent; parent = rb_parent(node); if (parent) continue; } break; } /* Case 3 - left rotate at sibling */ tmp1 = tmp2->rb_left; WRITE_ONCE(sibling->rb_right, tmp1); WRITE_ONCE(tmp2->rb_left, sibling); WRITE_ONCE(parent->rb_left, tmp2); if (tmp1) rb_set_parent_color(tmp1, sibling, RB_BLACK); augment_rotate(sibling, tmp2); tmp1 = sibling; sibling = tmp2; } /* Case 4 - right rotate at parent + color flips */ tmp2 = sibling->rb_right; WRITE_ONCE(parent->rb_left, tmp2); WRITE_ONCE(sibling->rb_right, parent); rb_set_parent_color(tmp1, sibling, RB_BLACK); if (tmp2) rb_set_parent(tmp2, parent); __rb_rotate_set_parents(parent, sibling, root, RB_BLACK); augment_rotate(parent, sibling); break; } } } /* Non-inline version for rb_erase_augmented() use */ void __rb_erase_color(struct rb_node *parent, struct rb_root *root, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { ____rb_erase_color(parent, root, augment_rotate); } //EXPORT_SYMBOL(__rb_erase_color); /* * 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 = { .propagate = dummy_propagate, .copy = dummy_copy, .rotate = dummy_rotate }; void rb_insert_color(struct rb_node *node, struct rb_root *root) { __rb_insert(node, root, dummy_rotate); } //EXPORT_SYMBOL(rb_insert_color); 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); } //EXPORT_SYMBOL(rb_erase); /* * 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); } //EXPORT_SYMBOL(__rb_insert_augmented); /* * This function returns the first node (in sort order) of the tree. */ 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; } //EXPORT_SYMBOL(rb_first); 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; } //EXPORT_SYMBOL(rb_last); 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->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. */ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent; return parent; } //EXPORT_SYMBOL(rb_next); 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->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. */ while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent; return parent; } //EXPORT_SYMBOL(rb_prev); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; /* Set the surrounding nodes to point to the replacement */ if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new); __rb_change_child(victim, new, parent, root); } //EXPORT_SYMBOL(rb_replace_node); void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; /* Set the surrounding nodes to point to the replacement */ if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new); /* Set the parent's pointer to the new node last after an RCU barrier * so that the pointers onwards are seen to be set correctly when doing * an RCU walk over the tree. */ __rb_change_child_rcu(victim, new, parent, root); } //EXPORT_SYMBOL(rb_replace_node_rcu); static struct rb_node *rb_left_deepest_node(const struct rb_node *node) { for (;;) { if (node->rb_left) node = node->rb_left; else if (node->rb_right) node = node->rb_right; else return (struct rb_node *)node; } } struct rb_node *rb_next_postorder(const struct rb_node *node) { const struct rb_node *parent; if (!node) return NULL; parent = rb_parent(node); /* If we're sitting on node, we've already seen our children */ if (parent && node == parent->rb_left && parent->rb_right) { /* If we are the parent's left node, go to the parent's right * node then all the way down to the left */ return rb_left_deepest_node(parent->rb_right); } else /* Otherwise we are the parent's right node, and the parent * should be next */ return (struct rb_node *)parent; } //EXPORT_SYMBOL(rb_next_postorder); struct rb_node *rb_first_postorder(const struct rb_root *root) { if (!root->rb_node) return NULL; return rb_left_deepest_node(root->rb_node); } //EXPORT_SYMBOL(rb_first_postorder);
3. 测试文件
/* * 使用 kernel 中的 rbtree_test.c 进行测试更好,其产生随机数据,并记录查找时间 */ #include <stdio.h> #include <stdlib.h> #include "rbtree.h" struct test_node { int key; struct rb_node rb; int val; }; static struct rb_root_cached rbtree_root = RB_ROOT_CACHED; static struct test_node *nodes = NULL; static void insert_cached(struct test_node *node, struct rb_root_cached *root) { struct rb_node **new = &root->rb_root.rb_node, *parent = NULL; int key = node->key; bool leftmost = true; while (*new) { parent = *new; if (key < rb_entry(parent, struct test_node, rb)->key) { new = &parent->rb_left; } else { new = &parent->rb_right; leftmost = false; //只要一次往右找了,新节点node就不可能是leftmost节点了 } } rb_link_node(&node->rb, parent, new); rb_insert_color_cached(&node->rb, root, leftmost); } struct test_node *search_cached(struct rb_root_cached *root, int key) { struct test_node *node_target = NULL; struct rb_node **new = &root->rb_root.rb_node, *parent = NULL; while (*new) { parent = *new; node_target = rb_entry(parent, struct test_node, rb); if (key < node_target->key) { new = &parent->rb_left; } else if (key > node_target->key) { new = &parent->rb_right; } else { return node_target; } } return NULL; } static void erase_cached(struct test_node *node, struct rb_root_cached *root) { rb_erase_cached(&node->rb, root); } static void test_init(struct rb_root_cached *root, int num) { int i, key, val; nodes = (struct test_node *)calloc(num, sizeof(struct test_node)); if (!nodes) { exit(-1); } printf("insert:\n"); for (i = 0; i < num; i++) { key = rand() % (num * 100); val = rand() % (num * 100); nodes[i].key = key; nodes[i].val = val; insert_cached(&nodes[i], root); printf("key=%d, val=%d\n", key, val); } } static void test_interator(struct rb_root_cached *root) { struct test_node *pos, *n; printf("\ninterator:\n"); rbtree_postorder_for_each_entry_safe(pos, n, &root->rb_root, rb) { printf("key=%d, val=%d\n", pos->key, pos->val); } pos = container_of(root->rb_leftmost, struct test_node, rb); printf("leftmost->key=%d, leftmost->val=%d\n", pos->key, pos->val); } static void test_interator_self_define(struct rb_root_cached *root) { struct rb_node *rn; struct test_node *pos; printf("\nspecial_pos:\n"); rn = rb_first(&root->rb_root); pos = container_of(rn, struct test_node, rb); printf("first: key=%d, val=%d\n", pos->key, pos->val); rn = rb_next(rn); pos = container_of(rn, struct test_node, rb); printf("next: key=%d, val=%d\n", pos->key, pos->val); rn = rb_last(&root->rb_root); pos = container_of(rn, struct test_node, rb); printf("last: key=%d, val=%d\n", pos->key, pos->val); rn = rb_prev(rn); pos = container_of(rn, struct test_node, rb); printf("prev: key=%d, val=%d\n", pos->key, pos->val); printf("\ninterator_self:\n"); rn = rb_first(&root->rb_root); while(rn) { rn = rb_next(rn); if (rn) { pos = container_of(rn, struct test_node, rb); printf("key=%d, val=%d\n", pos->key, pos->val); } } } static void test_search_erase(struct rb_root_cached *root, int num) { int i; struct test_node *node; //删除key值落在前50%的节点 printf("\ndelete:\n"); for (i = 0; i < num * 100 / 2; i++) { node = search_cached(root, i); if (node) { printf("key=%d, val=%d\n", node->key, node->val); erase_cached(node, root); } } } int main(int argc, char *argv[]) { int num = 100; if (argc == 2) { num = atoi(argv[1]); } test_init(&rbtree_root, num); test_interator(&rbtree_root); test_interator_self_define(&rbtree_root); test_search_erase(&rbtree_root, num); test_interator(&rbtree_root); free(nodes); return 0; }
实验结果:
/* $ ./pp 10 insert: key=383, val=886 key=777, val=915 key=793, val=335 key=386, val=492 key=649, val=421 key=362, val=27 key=690, val=59 key=763, val=926 key=540, val=426 key=172, val=736 interator: key=172, val=736 key=383, val=886 key=362, val=27 key=540, val=426 key=649, val=421 key=386, val=492 key=763, val=926 key=793, val=335 key=777, val=915 key=690, val=59 leftmost->key=172, leftmost->val=736 special_pos: first: key=172, val=736 next: key=362, val=27 last: key=793, val=335 prev: key=777, val=915 interator_self: key=362, val=27 key=383, val=886 key=386, val=492 key=540, val=426 key=649, val=421 key=690, val=59 key=763, val=926 key=777, val=915 key=793, val=335 delete: key=172, val=736 key=362, val=27 key=383, val=886 key=386, val=492 interator: key=649, val=421 key=540, val=426 key=763, val=926 key=793, val=335 key=777, val=915 key=690, val=59 leftmost->key=540, leftmost->val=426 */
4. 总结
使用 rbtree_postorder_for_each_entry_safe 这个宏遍历输出的值并非是按key的大小排序的,但是使用 rb_first 和 rb_next 获取到的是按 key 值大小排序的。
5. 补充
1. 插入时也可以不指定key,通过比较地址找到插入位置,举例如下。查找时可以通过 test_info 结构中的某个成员作为 key 进行查找。
//还可以没有key,直接使用地址作为key进行对比 void test_info_add(struct render_info *thr) { struct rb_node **p = &test_info_tree.rb_node; struct rb_node *parent = NULL; struct test_info *tmp = NULL; while (*p) { parent = *p; tmp = rb_entry(parent, struct render_info, linger_node); if (thr < tmp) p = &(*p)->rb_left; else if (thr > tmp) p = &(*p)->rb_right; else { return; //在红黑树中就直接返回了 } } rb_link_node(&thr->linger_node, parent, p); rb_insert_color(&thr->linger_node, &test_info_tree); }
2. 若不考虑 left_most, 可以 struct rb_root 和 rb_insert_color() 配合使用。
posted on 2021-10-02 20:19 Hello-World3 阅读(790) 评论(2) 编辑 收藏 举报