红黑树原理
红黑树是一种有序的平衡二叉树,5个性质:
1、 每个结点的颜色只能是红色或黑色。
2、 根结点是黑色的。
3、 每个叶子结点都带有两个空的黑色结点(被称为黑哨兵),如果一个结点n只有一个左孩子,那么n的右孩子是一个黑哨兵;如果结点n只有一个右孩子,那么n的左孩子是一个黑哨兵。
4、 如果一个结点是红的,则它的两个儿子都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。果然富不过二代,如果一个结点是黑的,那它是可以有黑儿子的。
5、 对于每个结点来说,从该结点到其子孙叶结点的所有路径上包含相同数目的黑结点。
性质4和5保证了,在一棵二叉查找树上,执行查找、插入、删除等操作的时间复杂度为O(lgn)。
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_RED 0 #define RB_BLACK 1
几个相关的宏定义:
//取出parent的指针,将__rb_parent_color的低2bit清0 #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) //取出颜色 #define __rb_color(pc) ((pc) & 1) #define rb_color(rb) __rb_color((rb)->__rb_parent_color)
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_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; }
void rb_insert_color(struct rb_node *node, struct rb_root *root) { __rb_insert(node, root, dummy_rotate); }
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); }
struct timerqueue_node { struct rb_node node; ktime_t expires; }; struct timerqueue_head { struct rb_root head; struct timerqueue_node *next; };
static inline void timerqueue_init_head(struct timerqueue_head *head) { head->head = RB_ROOT; head->next = NULL; } #define RB_ROOT (struct rb_root) { NULL, }
static inline void timerqueue_init(struct timerqueue_node *node) { RB_CLEAR_NODE(&node->node); } #define RB_CLEAR_NODE(node) \ ((node)->__rb_parent_color = (unsigned long)(node))
void timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node) { struct rb_node **p = &head->head.rb_node; struct rb_node *parent = NULL; struct timerqueue_node *ptr; /* Make sure we don't add nodes that are already added */ WARN_ON_ONCE(!RB_EMPTY_NODE(&node->node)); while (*p) { parent = *p; ptr = rb_entry(parent, struct timerqueue_node, node); if (node->expires.tv64 < ptr->expires.tv64) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&node->node, parent, p); rb_insert_color(&node->node, &head->head); if (!head->next || node->expires.tv64 < head->next->expires.tv64) head->next = node; }
(1) 如果node是刚刚初始化没有add过的,!RB_EMPTY_NODE(&node->node)为0;就不会有警告输出了。
(2) 找到爸爸,插入结点。由于hrtimer要求按照到期时间从小到大的顺序排序,所以我们从根root开始找,如果node的到期时间小,就在左子树中继续;如果node的到期时间大,就在右子树中继续;循环迭代,直到找到一个叶子结点结束,此时parent已经找到,它是一个叶子结点。
(3) rb_link_node()把node插入到树中,貌似插入的都是叶子结点。为什么这样就是插入了呢?传入的参数p中存的是2中找到的parent的一个孩子的二级指针,parent是个叶子结点,它的孩子基本就是个黑哨兵了,rb_link_node()中会*rb_link = node,这是让parent不再指向黑哨兵,而是指向node。
(4) rb_insert_color()是插入后做平衡调整。
(5) head->next = node;是因为hrtimer会用timerqueue_head->next存储最快到期的timerqueue_node,所以需要根据到期时间更新next。
void timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node) { WARN_ON_ONCE(RB_EMPTY_NODE(&node->node)); /* update next pointer */ if (head->next == node) { struct rb_node *rbn = rb_next(&node->node); head->next = rbn ? rb_entry(rbn, struct timerqueue_node, node) : NULL; } rb_erase(&node->node, &head->head); RB_CLEAR_NODE(&node->node); }(1) 是不是空树,是就给个警告。
struct timerqueue_node *timerqueue_iterate_next(struct timerqueue_node *node) { struct rb_node *next; if (!node) return NULL; next = rb_next(&node->node); if (!next) return NULL; return container_of(next, struct timerqueue_node, node); }利用rb_next()找,分前面说过的三种情况。