"Diseño e implementación del kernel de Linux" Notas de lectura: estructura de datos del kernel

Lista enlazada

Lista vinculada simple:

Lista doblemente enlazada:

Lista enlazada circular:

Si la operación principal en el conjunto de datos es recorrer los datos, use una lista vinculada.

 

Lista doblemente enlazada

Linux no rellena las estructuras de datos en la lista enlazada, sino que rellena los nodos de la lista enlazada en la estructura de datos.

Nodo de lista vinculada (incluye \ linux \ list.h):

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */
struct list_head {
    struct list_head *next, *prev;
};

list_head en sí no tiene ningún significado, debe estar incrustado en la estructura de datos para que surta efecto:

struct clk {
    struct list_head node;
    const char *name;
    int id;
    struct module *owner;
    struct clk *parent;
    struct clk_ops *ops;
    struct kref kref;
    unsigned long rate;
    unsigned long flags;
};

Utilice la macro container_of () para encontrar fácilmente los miembros en la estructura principal desde el nodo de la lista enlazada.

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({          \
    const typeof(((type *)0)->member) * __mptr = (ptr); \
    (type *)((char *)__mptr - offsetof(type, member)); })

La macro correspondiente con una lista vinculada:

/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

Se utiliza para devolver los miembros relacionados de la estructura de supertipo que contiene list_head.

Dependiendo de list_entry (), el kernel proporciona varias rutinas para crear, operar y otra administración de listas vinculadas.

 

Operación de lista vinculada

Inicialización del nodo de la lista vinculada:

#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

Inicialización del nodo de lista enlazada en la inicialización estática de la estructura de datos:

static struct ts_ops bm_ops = {
    .name         = "bm",
    .find         = bm_find,
    .init         = bm_init,
    .get_pattern      = bm_get_pattern,
    .get_pattern_len  = bm_get_pattern_len,
    .owner        = THIS_MODULE,
    .list         = LIST_HEAD_INIT(bm_ops.list)
};

Las listas doblemente enlazadas no suelen tener un encabezado, pero muchas veces es necesario especificar:

static LIST_HEAD(cpufreq_governor_list);

Entonces usa:

    list_for_each_entry(t, &cpufreq_governor_list, governor_list)
        if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN))
            return t;

Las operaciones relacionadas con la lista vinculada están todas en include \ linux \ list.h. Tenga en cuenta que las funciones de operación son todas funciones en línea, por lo que están en el archivo de encabezado.

No los presentaré uno por uno aquí.

 

cola

Cola primero en entrar último en salir:

Si el código se ajusta al patrón productor / consumidor, use colas.

 

Operación en cola

La implementación de la cola general del kernel de Linux se llama kfifo, se encuentra en kernel \ kfifo.c y se declara en include \ linux \ kfifo.h.

cola:

struct kfifo {
    unsigned char *buffer;  /* the buffer holding the data */
    unsigned int size;  /* the size of the allocated buffer */
    unsigned int in;    /* data is added at offset (in % size) */
    unsigned int out;   /* data is extracted from off. (out % size) */
};

Asignación dinámica de colas:

extern void kfifo_init(struct kfifo *fifo, void *buffer,
            unsigned int size);
extern __must_check int kfifo_alloc(struct kfifo *fifo, unsigned int size,
            gfp_t gfp_mask);

Asignación estática de la cola:

#define DECLARE_KFIFO(name, size) \
union { \
    struct kfifo name; \
    unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
}
#define INIT_KFIFO(name) \
    name = __kfifo_initializer(sizeof(name##kfifo_buffer) - \
                sizeof(struct kfifo), \
                name##kfifo_buffer + sizeof(struct kfifo))

Adivinar:

extern unsigned int kfifo_in(struct kfifo *fifo,
                const void *from, unsigned int len);

Mandar:

extern __must_check unsigned int kfifo_out(struct kfifo *fifo,
                void *to, unsigned int len);

 

Cartografía

El mapeo también se llama matriz asociativa.

El mapeo se refiere a la correspondencia entre valores clave.

La clave es única.

El mapeo implementado en Linux es un mapeo de un identificador único (UID, en realidad un valor de tipo int) a un puntero.

Linux proporciona una estructura de datos idr para mapear UID (incluye \ linux \ idr.h).

struct idr {
    struct idr_layer *top;
    struct idr_layer *id_free;
    int       layers; /* only valid without concurrent changes */
    int       id_free_cnt;
    spinlock_t    lock;
};

Si necesita mapear un UID a un objeto, use mapeo.

 

Operación de mapeo

Inicializar un idr:

void idr_init(struct idr *idp);

Para obtener el UID, hay dos pasos:

int idr_pre_get(struct idr *idp, gfp_t gfp_mask);
int idr_get_new(struct idr *idp, void *ptr, int *id);

El UID se almacena en id y está asociado con el puntero ptr.

Encuentra UID:

void *idr_find(struct idr *idp, int id);

Eliminar UID:

void idr_remove(struct idr *idp, int id);

Destruir idr:

void idr_destroy(struct idr *idp);

Sin embargo, la memoria asignada a UID no se borrará a menos que se utilice:

void idr_remove_all(struct idr *idp);

 

árbol

La estructura de árbol es una estructura de datos en forma de árbol que puede proporcionar una jerarquía.

Un árbol binario tiene como máximo dos bordes salientes por nodo.

Un árbol de búsqueda binario es un árbol binario con nodos ordenados:

  • El valor del nodo de la rama izquierda de la raíz es menor que el valor del nodo raíz;
  • El valor del nodo de la rama derecha es mayor que el valor del nodo raíz;
  • Todos los subárboles también son árboles de búsqueda binarios;

La diferencia de profundidad de todos los nodos hoja del árbol de búsqueda binaria autoequilibrado no supera 1.

El árbol rojo-negro es un árbol de búsqueda binario autoequilibrado, que tiene las siguientes especificaciones:

  • Todos los nodos son rojos o negros;
  • Los nudos de las hojas son todos negros;
  • Los nodos hoja no contienen datos;
  • Todos los nodos que no son hojas tienen dos nodos secundarios;
  • Si un nodo es rojo, sus nodos secundarios son todos negros;
  • En la ruta de un nodo a su nodo hoja, si siempre contiene el mismo número de nodos negros, la ruta es la más corta en comparación con otras rutas;

Si necesita almacenar una gran cantidad de datos y recuperarlos rápidamente, use árboles rojo-negro.

 

Implementación de árbol rojo-negro

La principal estructura de datos de árbol binario equilibrado de Linux es el árbol rojo-negro.

El árbol rojo-negro implementado por Linux se llama rbtree.

El nodo raíz está representado por rb_root (incluye \ linux \ rbtree.h):

struct rb_root
{
    struct rb_node *rb_node;
};

Otros nodos están representados por rb_node:

struct rb_node
{
    unsigned long  rb_parent_color;
#define RB_RED      0
#define RB_BLACK    1
    struct rb_node *rb_right;
    struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));

La implementación de rbtree no proporciona rutinas de búsqueda e inserción.

 

Supongo que te gusta

Origin blog.csdn.net/jiangwei0512/article/details/106143604
Recomendado
Clasificación