wandres.dev
ESTRUCTURAS DE DATOS · list_head, rbtree, xarray

Árboles, hashes y otras estructuras

Más allá de las listas: árboles rojo-negro para datos ordenados, hash tables para búsqueda rápida, y xarray/idr para mapear enteros a punteros. El kit de datos del kernel.

⏱ 11 min

Las listas son geniales para recorrer, pero lentas para buscar. El kernel provee estructuras optimizadas —árboles equilibrados, tablas hash, arrays dispersos— todas con el mismo diseño intrusivo. Conocerlas te deja elegir la estructura correcta en vez de reinventarla.

🎯 Al terminar esta lección sabrás
  • Árboles rojo-negro (rb_node).
  • Tablas hash del kernel.
  • xarray e idr para mapear enteros.
  • Elegir la estructura adecuada.

Árboles rojo-negro

Cuando necesitas datos ordenados con inserción, borrado y búsqueda en O(log n), el kernel usa árboles rojo-negro (self-balancing). Igual que las listas, son intrusivos: embebes un struct rb_node:

#include <linux/rbtree.h>

struct evento {
    u64 timestamp;
    struct rb_node nodo;    // el nodo del árbol, embebido
};
static struct rb_root arbol = RB_ROOT;

Los usa el planificador (nivel 25) para ordenar tareas, el gestor de memoria para las regiones virtuales, y cualquier sitio que necesite orden con búsqueda rápida. Insertar requiere escribir la comparación (dónde va cada nodo), pero el reequilibrado lo hace el kernel.

Tablas hash

Para búsqueda por clave en O(1) medio, las hash tables del kernel (DECLARE_HASHTABLE) enlazan objetos en cubetas, otra vez de forma intrusiva:

#include <linux/hashtable.h>
DECLARE_HASHTABLE(mi_tabla, 8);     // 2^8 = 256 cubetas

struct entrada {
    int clave;
    struct hlist_node nodo;         // nodo de hash, embebido
};
hash_add(mi_tabla, &e->nodo, e->clave);

xarray e idr: enteros → punteros

Un patrón constante en el kernel: “tengo un ID entero, quiero el objeto asociado” (un descriptor de archivo → su struct file, un PID → su task). El xarray (y el más antiguo idr) resuelven esto eficientemente, incluso para IDs dispersos:

#include <linux/xarray.h>
DEFINE_XARRAY(mis_objetos);
xa_store(&mis_objetos, id, objeto, GFP_KERNEL);
void *obj = xa_load(&mis_objetos, id);
El kernel te da las piezas; elige con criterio

Una marca del buen ingeniero de kernel es no reinventar estructuras de datos. El kernel lleva décadas puliendo implementaciones de listas, árboles, hashes y arrays que son correctas, concurrentes-conscientes y rapidísimas — probadas en los sistemas más exigentes del mundo. Tu trabajo no es escribir un árbol rojo-negro (casi seguro con bugs); es elegir la estructura correcta para tu problema y usar la del kernel: ¿necesito recorrer en orden de inserción? Lista. ¿Datos ordenados con búsqueda? rbtree. ¿Búsqueda por clave arbitraria? hash. ¿Mapear un ID a un objeto? xarray. Todas comparten el diseño intrusivo (nivel 10.1), así que una vez entiendes container_of, todas te resultan familiares. Saber qué estructura existe y cuándo usarla —el “vocabulario de datos” del kernel— es lo que separa el código torpe del idiomático.

⚔️ Elige la estructura correcta
  1. Para cada caso, di qué estructura usarías: (a) cola de trabajos en orden, (b) eventos ordenados por tiempo con búsqueda, (c) buscar un objeto por un ID entero.
  2. Declara una hash table con DECLARE_HASHTABLE y añade entradas.
  3. Explora en el código del kernel un uso real de rb_node (p. ej. en mm/).
  4. Investiga cómo el kernel mapea un descriptor de archivo a su struct file.