Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1828637
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-02-17 14:57:59

linux 内存管理之红黑树
 
谨以此文纪念过往的岁月。
 
我们可以将经常需要被读取的数据定义为 __read_mostly类型, 这样Linux内核被加载时,该数据将自动被存放到Cache中,以提高整个系统的执行效率
一.红黑树
1.1什么是红黑树
红黑树是满足一定特征的二叉树,其本质还是二叉树。
一般的,红黑树,满足以下性质,即只有满足以下全部性质的树,我们才称之为红黑树:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
其每一个红黑树的节点包括三个参数node_color,node_left,node_right。
http://blog.csdn.net/v_JULY_v/archive/2010/12/29/6105630.aspx 博文详细的讲述了红黑树的操作。
1.2linux中如何实现红黑树
1.2.1红黑树结构体
 __attribute__((aligned(sizeof(long))));   --声明结构体long类型对齐。
红黑根节点描述
struct rb_root
{
 struct rb_node *rb_node;
};
红黑树节点描述
struct rb_node
{
 unsigned long  rb_parent_color;     --保存节点color同时保存父节点的指针值
#define RB_RED  0
#define RB_BLACK 1
 struct rb_node *rb_right;   --右子
 struct rb_node *rb_left;    --左子
}
设置父节点。
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
{
 rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
}
设置节点颜色
static inline void rb_set_color(struct rb_node *rb, int color)
{
 rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
}
1.2.2红黑树的操作
红黑树的最主要特征在于其颜色满足特定的性质。普通的节点添加,极有可能破坏红黑树的性质,所以在添加红黑树节点时,需要将整个红黑树的颜色进行调整。
下面以rb_insert_color为例。
在理解下面的程序前要先理解两个函数,即红黑树的左旋和右旋。
左旋:
对照上图可以更好的理解下面的程序。将node设置为pivot,root不管。
static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{
 struct rb_node *right = node->rb_right;      --right = Y
 struct rb_node *parent = rb_parent(node);    --parent = P
 if ((node->rb_right = right->rb_left))    --pivot的右子设置为P的左子即pivot->rb_right = b
  rb_set_parent(right->rb_left, node);    --同时将b->parent 设置为pivot
 right->rb_left = node;                    --将pivot设置为Y的左子
 rb_set_parent(right, parent);             --设置Y的parent为P
 if (parent)
 {
  if (node == parent->rb_left)            --如果pivot为P的左子
   parent->rb_left = right;              --则P的右子设置为Y
  else
   parent->rb_right = right;             --否则P的左子设置为Y
 }
 else
  root->rb_node = right;                
 rb_set_parent(node, right);             --设pivot的parent为Y
}
对于右旋参照左旋即可了,这两个旋转类似。

static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
{
 struct rb_node *left = node->rb_left;
 struct rb_node *parent = rb_parent(node);
 if ((node->rb_left = left->rb_right))
  rb_set_parent(left->rb_right, node);
 left->rb_right = node;
 rb_set_parent(left, parent);
 if (parent)
 {
  if (node == parent->rb_right)
   parent->rb_right = left;
  else
   parent->rb_left = left;
 }
 else
  root->rb_node = left;
 rb_set_parent(node, left);
}
关于左旋和右旋都是为了保证平衡二叉树的性质不变,即是通过左旋和右旋来保证红黑二叉树的第五条性质满足。
红黑二叉树的添加跟普通的二叉树的添加类似。不过在添加节点后需要对节点的颜色进行调整,甚至对树的结构进行调整,来满足红黑树的性质。
在博文http://blog.csdn.net/zhongjiekangping/archive/2010/05/26/5624503.aspx中对红黑树插入的情况做了详细的描述。
 
 

将下面函数对比上三幅图即可。
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
 struct rb_node *parent, *gparent;   --一个为父节点,一个为祖父节点
 while ((parent = rb_parent(node)) && rb_is_red(parent))  --从node获得父节点指针值,并判断父节点是否为红色
 {
  gparent = rb_parent(parent);
  if (parent == gparent->rb_left)
  {
   {
    register struct rb_node *uncle = gparent->rb_right;
    if (uncle && rb_is_red(uncle))           
    {
     rb_set_black(uncle);
     rb_set_black(parent);
     rb_set_red(gparent);
     node = gparent;
     continue;
    }
   }
   if (parent->rb_right == node)
   {
    register struct rb_node *tmp;
    __rb_rotate_left(parent, root);
    tmp = parent;
    parent = node;
    node = tmp;
   }
   rb_set_black(parent);
   rb_set_red(gparent);
   __rb_rotate_right(gparent, root);
  } else {
   {
    register struct rb_node *uncle = gparent->rb_left;
    if (uncle && rb_is_red(uncle))
    {
     rb_set_black(uncle);
     rb_set_black(parent);
     rb_set_red(gparent);
     node = gparent;
     continue;
    }
   }
   if (parent->rb_left == node)
   {
    register struct rb_node *tmp;
    __rb_rotate_right(parent, root);
    tmp = parent;
    parent = node;
    node = tmp;
   }
   rb_set_black(parent);
   rb_set_red(gparent);
   __rb_rotate_left(gparent, root);
  }
 }
 rb_set_black(root->rb_node);
}
在理解红黑二叉树后,再来理解内存管理。在linux中虚拟内存的vma采用红黑树的结构来管理。这也是为什么要去理解红黑树的结构。
二.虚拟内存管理
在start_kernel中有一段关于vma的初始化
vmalloc_init -> __insert_vmap_area
将已有的vma添加入管理。
static void __insert_vmap_area(struct vmap_area *va)
{
 struct rb_node **p = &vmap_area_root.rb_node;
 struct rb_node *parent = NULL;
 struct rb_node *tmp;
 while (*p) {            --查询适合的插入点
  struct vmap_area *tmp;
  parent = *p;
  tmp = rb_entry(parent, struct vmap_area, rb_node);
  if (va->va_start < tmp->va_end)
   p = &(*p)->rb_left;
  else if (va->va_end > tmp->va_start)
   p = &(*p)->rb_right;
  else
   BUG();
 }
 rb_link_node(&va->rb_node, parent, p);  --将节点插入
 rb_insert_color(&va->rb_node, &vmap_area_root);  --调整红黑二叉树。
 tmp = rb_prev(&va->rb_node);                    
 if (tmp) {
  struct vmap_area *prev;
  prev = rb_entry(tmp, struct vmap_area, rb_node);
  list_add_rcu(&va->list, &prev->list);
 } else
  list_add_rcu(&va->list, &vmap_area_list);
}
上面是vmalloc_init。
阅读(4180) | 评论(0) | 转发(0) |
0

上一篇:定时器

下一篇:input子系统(1)

给主人留下些什么吧!~~