阐述Linux内存管理:红黑树

系统 Linux
很多的人都开始学习Linux操作系统。当我们学习Linux时,会遇到很多的问题。最近看Linux内存管理,看到红黑树这一部分甚为头大,于是了解了一下红黑树的基本知识。详细讲解一下Linux内存管理。

很多的人都开始学习Linux操作系统。当我们学习Linux时,会遇到很多的问题。最近看Linux内存管理,看到红黑树这一部分甚为头大,于是了解了一下红黑数的基本知识。详细讲解一下Linux内存管理。

红黑树是平衡二叉树的一种,它有很好的性质,树中的结点都是有序的,而且因为它本身就是平衡的,所以查找也不会出现非常恶劣的情况,基于二叉树的操作的时间复杂度是O(log(N))。Linux内核在管理vm_area_struct时就是采用了红黑树来维护内存块的。

先到include/Linux/rbtree.h中看一下红黑树的一些定义,如下:

  1. struct rb_node  
  2. {  
  3. struct rb_node* rb_parent; 该节点的父节点  
  4. int rb_color;  该节点的颜色  
  5. #define RB_RED 0  
  6. #define RB_BLACK 1  
  7. struct rb_node *rb_right; 该节点的右子节点  
  8. struct rb_node *rb_left;  该节点的左子节点  
  9. } __attribute__((aligned(sizeof(long)))); 

struct rb_root只是struct rb_node*的一个包装,这样做的好处是看起来不用传递二级指针了。不错,很简单。

测试颜色和设置颜色也是水到渠成的事了。需要特别指出的是下面的一个内联函数:
static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link);

它把parent设为node的父结点,并且让rb_link指向node。

我们把重点集中在lib/rbtree.c上,看看一些和红黑树相关的重要算法。开始之前我们一起回忆一下红黑树的规则:

1. 每个结点要么是红色要么是黑色;
2. 根结点必须是黑色;
3. 红结点如果有孩子,其孩子必须都是黑色;
4. 从根结点到叶子的每条路径必须包含相同数目的黑结点。
5、每个树节点N的左孩子树上的所有元素都是排在N之前,右孩子树子上的所有元素都是排在N之后

这四条规则可以限制一棵排序树是平衡的。

__rb_rotate_left是把以root为根的树中的node结点进行左旋,__rb_rotate_right是进行右旋。这两个函数是为后面的插入和删除服务,而不是为外部提供接口。

新插入的结点都设为叶子,染成红色,插入后如果破坏了上述规则,通过调整颜色和旋转可以恢复,二叉树又重新平衡。插入操作的接口函数是
void rb_insert_color(struct rb_node *node, struct rb_root *root);

它把已确定父结点的node结点融入到以root为根的红黑树中,具体算法的分析可以参考[1]中第14.3节,这里的实现和书中的讲解几乎完全一样。怎么确定node的父结点应该在调用rb_insert_color之前通过手工迭带完成。值得指出的一点是,虽然插入操作需要一个循环迭代,但是总的旋转次数不会超过两次!所以效率还是很乐观的。

删除操作多多少少都有点麻烦,它要先执行像普通二叉查找树的“删除”,然后根据删除结点的颜色来判断是否执行进一步的操作。删除的接口是:
void rb_erase(struct rb_node *node, struct rb_root *root);

其实它并没有真正删除node,而只是让它和以root为根的树脱离关系,最后它还要判断是否调用__rb_erase_color来调整。具体算法的讲解看参考[1]中第13.3和14.4节,__rb_erase_color对应书中的RB-DELETE-FIXUP,此处的实现和书上也基本上一致。

其余的几个接口就比较简单了。
struct rb_node *rb_first(struct rb_root *root);

在以root为根的树中找出并返回最小的那个结点,只要从根结点一直向左走就是了。
struct rb_node *rb_last(struct rb_root *root);

是找出并返回最大的那个,一直向右走。
struct rb_node *rb_next(struct rb_node *node);

返回node在树中的后继,这个稍微复杂一点。如果node的右孩子不为空,它只要返回node的右子树中最小的结点即可;如果为空,它要向上查找,找到迭带结点是其父亲的左孩子的结点,返回父结点。如果一直上述到了根结点,返回NULL。
struct rb_node *rb_prev(struct rb_node *node);

返回node的前驱,和rb_next中的操作对称。
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);

用new替换以root为根的树中的victim结点。
因为红黑树的这些良好性质和实现中接口的简易性,它被广泛应用到内核编程中,大大提高了内核的效率。

通过本文的介绍你就能熟练掌握Linux内存管理。

【编辑推荐】

  1. 掌握操作技巧 玩转Linux桌面操作系统
  2. 各种各样的Linux来自Linux个人开发者
  3. 伦敦证券启用Linux操作系统平台
  4. Linux基金会大动作
  5. Linux操作系统:普通用户安全执行管理员程序
责任编辑:小霞 来源: 51CTO
相关推荐

2020-09-17 07:37:09

红黑树数据结构

2009-12-25 17:15:03

Linux内存

2016-12-08 11:01:39

红黑树Java

2020-07-09 07:00:00

HashMap

2020-11-05 09:03:32

红黑树面试数据

2020-10-09 06:56:55

红黑树动图二叉树

2010-07-19 14:17:47

SQL Server内

2019-08-22 09:22:44

数据结构二叉搜索树

2020-11-05 13:12:47

红黑树

2009-12-21 14:33:11

2023-03-31 08:24:29

数据结构算法数目

2023-08-29 08:31:13

B+树数据索引

2020-05-06 16:41:36

红黑树二叉查找树

2020-03-11 08:40:51

红黑树平衡二叉B树

2019-10-12 08:36:48

Java程序员数据结构

2009-12-16 15:59:13

Linux设备文件管理

2019-09-23 11:35:23

数据结构设计红黑树

2019-01-22 09:37:47

红黑树数据二叉树

2021-03-19 07:59:33

红黑树面试数据

2020-08-19 16:36:53

HashMap红黑树阈值
点赞
收藏

51CTO技术栈公众号