【Kernel】内核数据结构之二叉树(v5.15.10)
一、概念与特点
- 树结构是一个能提供分层的树型数据结构的特定数据结构。在数学意义上,树是一个无环的、连接的有向图,其中任何一个顶点(在树里叫节点)具有0个或多个出边以及0个或1个入边。一个二叉树是每个节点最多只有两个出边的树,也就是一个树其节点具有0个、1个或者2个子节点。
二、二叉搜索树(BST)、自平衡二叉树
-
二叉搜索树:一个BST是一个节点有序的二叉树,满足以下条件
- 根的左分支节点值都小于根节点值
- 根的右分支节点值都大于根节点值
- 所有的子树也都是二叉搜索树
-
自平衡二叉树(Linux主要的平衡二叉树数据结构就是红黑树)
-
红黑树
- 所有节点要么着红色,要么着黑色
- 叶子节点都是黑色
- 所有非叶子节点都有两个子节点
- 若一个节点是红色,则子节点都是黑色
- 在一个节点到其叶子节点的路径中,如果总是包含同样数目的黑色节点,则该路径相比其他路径是最短的
红黑树的性质是每条路径的黑色节点数目相同,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡。
-
三、内核二叉树:rbtree
linux实现的红黑树成为rbtree,其声明和定义在以下几个文件下。
include/linux/rbtree_types.h
include/linux/rbtree.h
lib/rbtree.c
下面简单介绍一下其结构,rbtree的结构由rb_node和rb_root组成。rb_node结构体四字节对齐,因而其地址中低位两个bit永远是0。 Linux内核开发人员非常爱惜内存,他们用其中一个空闲的位来存储颜色信息。__rb_parent_color成员实际上包含了父节点指针和自己的颜色信息。
字节对齐和rb_parent_color 是用来存父节点指针和自己的颜色信息的原因:
linux的红黑树是用一个 unsigned long类型来存储指针和保存该节点的color 源码中结构体用 'attribute((aligned(sizeof(long))))'包装,字节对齐的作用:
①对于32位机,sizeof(long)为4 (结构体内的数据类型的地址为4的倍数)
②对于64位机,sizeof(long)为8(结构体内的数据类型的地址为8的倍数)
一个变量的内存地址正好位于它长度的整数倍,他就被称做字节对齐。
至于为什么要字节对齐:CPU访问数据的效率问题
所以无论是32位的还是64位的最后两位,RB_node结构体的地址的低两位(或者四位)肯定都是零,与其空着不用,还不如用它们表示颜色,反正颜色就两种,其实一位就已经够了。
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_ROOT (struct rb_root) { NULL, }
#define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL }
rbtree的根节点由数据结构rb_root描述,创建一个红黑树,要分配一个新的rb_root结构,并需要初始化为特殊值RB_ROOT。如:
struct rb_root root = RB_ROOT;
树里的其他节点由结构rb_node描述。给定一个rb_node,可以通过跟踪同名节点指针来找到其左右节点。
其他rbtree详细内容见文末参考文章。