[Estructura de datos de programación de Linux] rbtree

 

 

Rbtree del kernel de Linux

Debido a que se necesita rbtree para registrar solicitudes asincrónicas al escribir módulos del kernel, he estudiado y analizado el uso de kernel rbtree, que se registra aquí. Este artículo se refiere principalmente al documento del kernel rbtree.txt

Introducción a rbtree

Los árboles rojo-negro (rbtree) es un árbol de búsqueda binario autoequilibrado que se utiliza para almacenar pares de datos clave / valor categorizables. Es diferente de los árboles de radix o las tablas hash.
Los árboles de radix se utilizan para almacenar de forma eficaz matrices dispersas (utilizando índices enteros largos para la inserción, consulta y eliminación de nodos) Los valores de índice son demasiado grandes para ser almacenados directamente en la matriz.
Las tablas hash se utilizan para hacer hash en los índices para reducir el alcance de las consultas, pero no están ordenadas, por lo que no se pueden ubicar rápidamente.

Los árboles rojo-negro son similares a los árboles AVL, pero proporcionan un rendimiento de inserción y eliminación en tiempo real más rápido en el peor de los casos. Reequilibre el árbol insertando hasta 2 rotaciones y eliminando hasta 3 rotaciones. Sin embargo, en comparación con los árboles AVL, su tiempo de consulta es ligeramente más lento (O (log n)).

El kernel de Linux usa rbtree ampliamente, como: la fecha límite del algoritmo de programación de E / S y CFQ usan rbtree para rastrear las solicitudes; el código del temporizador de alta precisión usa rbtree para organizar las tareas de tiempo; el sistema de archivos ext3 usa rbtree para rastrear las entradas del directorio; y así sucesivamente.

Cómo usar rbtree

La implementación del kernel rbtree está en el archivo "lib / rbtree.c". Para usar rbtree, debe incluir el archivo de encabezado:

#include <linux/rbtree.h>

Para mejorar el rendimiento, rbtree de Linux implementa menos capas intermedias que los árboles tradicionales. La estructura de nodo struct rb_node de rbtree está directamente incrustada en la estructura de datos del usuario (el método tradicional es apuntar a la estructura de datos a través de un puntero). Las funciones de inserción y consulta de rbtree son implementadas por los propios usuarios llamando a las funciones básicas proporcionadas por linux rbtree (el método tradicional es proporcionar punteros de función de devolución de llamada). Y el bloqueo de btree también lo gestiona el usuario.

Crear rbtree

Defina struct rb_node en la estructura de datos:

struct mytype {
	struct rb_node node;
	char *keystring;
};

Al procesar los nodos rbtree, container_of()busque el puntero de la estructura de datos a través de la definición de macro. keystringLa clave de rbtree se puede definir como una cadena o un entero, y se utilizará para la clasificación y búsqueda definidas por el usuario.

Luego defina el nodo raíz de rbtree:

struct rb_root mytree = RB_ROOT;

Encuentra rbtree

El usuario implementa la función de búsqueda rbtree por sí mismo, a través del siguiente método: comenzando desde la raíz, comparando el valor de la clave y luego buscando el nodo izquierdo o el nodo derecho según sea necesario.

struct mytype *my_search(struct rb_root *root, char *string)
{
	struct rb_node *node = root->rb_node;
	while (node) {
		struct mytype *data = container_of(node, struct mytype, node);
		int result;
		result = strcmp(string, data->keystring);
		if (result < 0)
			node = node->rb_left;
		else if (result > 0)
			node = node->rb_right;
		else
			return data;
	}
	return NULL;
}

Insertar nuevo nodo

El usuario implementa la función de inserción de rbtree por sí mismo, primero encuentra la posición de inserción (la posición es NULL), luego inserta un nuevo nodo y realiza el reequilibrio de rbtree. Cuando se encuentra la posición de inserción, se requiere el enlace de su nodo principal para reequilibrar rbtree.

int my_insert(struct rb_root *root, struct mytype *data)
{
	struct rb_node **new = &(root->rb_node), *parent = NULL;
	/* Figure out where to put new node */
	while (*new) {
		struct mytype *this = container_of(*new, struct mytype, node);
		int result = strcmp(data->keystring, this->keystring);
		parent = *new;
		if (result < 0)
			new = &((*new)->rb_left);
		else if (result > 0)
			new = &((*new)->rb_right);
		else
			return FALSE;
	}
	/* Add new node and rebalance tree. */
	rb_link_node(&data->node, parent, new);
	rb_insert_color(&data->node, root);
	return TRUE;
}

Eliminar / sobrescribir nodo

Elimina y sobrescribe un nodo con las siguientes funciones:

void rb_erase(struct rb_node *victim, struct rb_root *tree);
void rb_replace_node(struct rb_node *old, struct rb_node *new, struct rb_root *tree);

Cubrir un nodo no reequilibrará el rbtree, por lo que debe asegurarse de que las claves nuevas y antiguas sean las mismas; de lo contrario, se producirá una excepción.
Eliminar un ejemplo de código de nodo:

struct mytype *data = mysearch(&mytree, "walrus");
if (data) {
	rb_erase(&data->node, &mytree);
	myfree(data);
}

Atravesar rbtree en orden

Las siguientes 4 funciones se utilizan para recorrer secuencialmente el rbtree:

struct rb_node *rb_first(struct rb_root *tree);
struct rb_node *rb_last(struct rb_root *tree);
struct rb_node *rb_next(struct rb_node *node);
struct rb_node *rb_prev(struct rb_node *node);

Ejemplo de código:

struct rb_node *node;
for (node = rb_first(&mytree); node; node = rb_next(node))
	printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring);

rbtree.h中删除#include 和#include 两行,添加#include

对于rb_node的声明删除掉最后的__attribute__((aligned(sizeof(long))))

最后在rbtree.h中添加如下一些宏定义:

#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#define container_of(ptr, type, member) ({            /
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
        (type *)( (char *)__mptr - offsetof(type,member) );})

 

在使用rbtree的时候,每个rbtree的元素都由使用者自己定义如:
struct my_item
{
  struct rb_node node;
  struct my_data_struct data;
};
其中的struct my_data_struct是自己的数据区域
struct my_data_struct
{
  long key;
  long value;
};

下面需要实现两个函数,第一个是一个search函数,形式如下:
struct my_item* _my_rbtree_search (struct rb_root* root, long key);
其中的root参数是根节点指针;key是关键字,rbtree根据它来排序以及查找。
该函数的实现如下:
struct my_item*
_my_rbtree_search (struct rb_root* root, long key)
{
  struct rb_node *node;
  struct my_item* item;
  node = root->rb_node;

  while (node)
    {
      item = rb_entry (node, struct my_item, node);
      if (item->data.key > key)
        node = node->rb_left;
      else if (item->data.key < key)
        node = node->rb_right;
      else
        {
          return item;            /* found it */
        }
    }
  return NULL;
}

下面还需要实现一个插入的函数:
void
_my_rbtree_insert (struct rb_root* root, struct my_item* item)
{
  struct rb_node **link, *parent;
  long value;
  struct my_item* p_item;
  link = &(root->rb_node);
  value = item->data.key;
  /* Go to the bottom of the tree */
  while (*link)
    {
      parent = *link;
      p_item = rb_entry(parent, struct my_item, node);
      if (p_item->data.key > value)
        link = &((*link)->rb_left);
      else if (p_item->data.key < value)
        link = &((*link)->rb_right);
      else
        return;
    }
  /* Put the new node there */
  rb_link_node(&(item->node), parent, link);
  rb_insert_color(&(item->node), root);
}

拥有了以上两个函数我们就可以实现search、insert、remove、modify、traverse等操作,下面给出大概的伪代码吧
struct my_data_struct search (struct rb_root* root, long key)
{
  struct my_data_struct data;
  struct my_item *item;
  memset (&data, 0, sizeof(struct my_data));
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    memcpy (&data, item->data, sizeof(struct my_data_struct));
  }
  return data;
}

int insert (struct rb_root* root, long key, long value)
{
  struct my_item  *item = (struct my_item*) malloc (sizeof(struct my_item));
  if (NULL == item)
  {
    return -1;
  }
  item->data.key = key;
  item->data.value = value;
  _my_rbtree_insert (root, item);
  return 0;
}

int remove (struct rb_root* root, long key)
{
  struct my_item* item;
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    rb_erase (&(item->node), root);
  }
  return 0;
}

int modify (struct rb_root* root, long key, long new_value)
{
  struct my_item* item;
  item = _my_rbtree_search (root, key);
  if (NULL != item)
  {
    item->data.value = new_value;
  }
  return 0;
}

int traverse (struct rb_root* root)
{
  struct rb_node* node;
  struct my_item* item;
  for (node = rb_first (root); node; node = rb_next (node))
  {
    item = rb_entry (node, struct my_item, node);
    printf ("key:%d/tvalue:%d/n", item->data.key, item->data.value);
  }
  return 0;
}

 

Supongo que te gusta

Origin blog.csdn.net/star871016/article/details/109311872
Recomendado
Clasificación