Linux内核--链表结构(一)

一、前言

Linux内核链表结构是一种双向循环链表结构,与传统的链表结构不同,Linux内核链表结构仅包含前驱和后继指针,不包含数据域。使用链表结构,仅需在结构体成员中包含list_head*成员就行;链表结构的定义在linux/list.h头文件。

二、链表初始化

struct list_head {
    struct list_head *next, *prev;
};

#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;
}

宏LIST_HEAD_INIT(name)和LIST_HEAD(name)的作用在于初始化一个链表头节点,并使其前驱指针和后继指针指向自身;内联函数INIT_LIST_HEAD同理;

img

三、添加节点

static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}

list_add:在头节点后插入节点,图示如下,node2为新增的节点:

img

list_add_tail在头节点前插入节点,图示如下,node2为新增的节点:

img

四、删除节点

static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}
static inline void list_del_init(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}

list_del:删除链表中的entry节点,entry节点的前驱后继指针指向LIST_POSITION1和LIST_POSITION2两个特殊值,这样设置是为了保证不在链表中的节点项不可访问,对LIST_POSITION1和LIST_POSITION2的访问都将引起页故障。

list_del_init:删除原链表中的entry节点,然后重新初始化entry节点为头节点(使其前驱后继指针都指向自身)。

/*
 * Architectures might want to move the poison pointer offset
 * into some well-recognized area such as 0xdead000000000000,
 * that is also not mappable by user-space exploits:
 */
#ifdef CONFIG_ILLEGAL_POINTER_VALUE
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
#else
# define POISON_POINTER_DELTA 0
#endif

/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)

链表删除的图示如下:

img

五、节点替换

static inline void list_replace(struct list_head *old,
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}

static inline void list_replace_init(struct list_head *old,
                    struct list_head *new)
{
    list_replace(old, new);
    INIT_LIST_HEAD(old);
}

list_replace:将旧节点替换为新节点,函数头两句对应下图2,新节点next指针指向node1,node1节点的prev指针指向新节点。后两句对应图3,新节点prev指针指向head,head节点的next指针指向新节点。此时old节点的next和prev指针指向仍保留着;
list_replace_init:将旧节点替换为新节点,并将旧节点重新初始化为头节点(前驱后继指针指向自身),对应下图4。

img

六、移动节点

static inline void list_move(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add(list, head);
}
static inline void list_move_tail(struct list_head *list,
                  struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
}

list_move:将list节点移动至head节点后(对应下图示的node1节点移动);

img

list_move_tail:将list节点移动至head节点前(对应下图示的node2节点移动);

img

七、尾节点判断

static inline int list_is_last(const struct list_head *list,
                const struct list_head *head)
{
    return list->next == head;
}

链表的最后一个节点特性:其后继指针next必将指向头节点head

八、链表空判断

static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
}
static inline int list_empty_careful(const struct list_head *head)
{
    struct list_head *next = head->next;
    return (next == head) && (next == head->prev);
}

list_empty和list_empty_careful都是判断链表是否为空。list_empty判断节点的后继指针next是否指向自身;list_empty_careful判断节点的后继指针和前驱指针是否均指向自身,其可用来判断链表是否为空且当前是否正在被修改。

九、链表旋转

static inline void list_rotate_left(struct list_head *head)
{
    struct list_head *first;

    if (!list_empty(head)) {
        first = head->next;
        list_move_tail(first, head);
    }
}

list_rotate_left:链表节点向左移动,原先左边的节点向右移。相当于与前一节点互换位置。图示如下:

img

十、拆分链表

static inline void __list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}

/**
 * list_cut_position - cut a list into two
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @entry: an entry within head, could be the head itself
 *    and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
 * pass on @entry an element you know is on @head. @list
 * should be an empty list or a list you do not care about
 * losing its data.
 *
 */
static inline void list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    if (list_empty(head))
        return;
    if (list_is_singular(head) &&
        (head->next != entry && head != entry))
        return;
    if (entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list, head, entry);
}

链表初始状态如下:

img

插入list节点:

img

修改head和entry->next(这里是node4)节点的前驱后继指向:

img

即:

img

函数参数list是指要加进来的链表,head是指要拆分的链表头节点,entry则是位于head指向的链表中的某个节点;

函数的作用是将head(不包括head节点)到entry的链表拆分下来,添加到list所指向的链表后;

如果链表为空或entry指向的就是头节点,亦或者链表仅单个节点且entry这个节点不在这个链表内(不指向head亦不指向head->next),则不能拆分。

十一、判断链表是否仅含单个节点

static inline int list_is_singular(const struct list_head *head)
{
    return !list_empty(head) && (head->next == head->prev);
}

判断条件为链表不为空,且头指针的前驱和后继均指向同个节点

十二、合并链表

static inline void __list_splice(const struct list_head *list,
                 struct list_head *prev,
                 struct list_head *next)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;

    first->prev = prev;
    prev->next = first;

    last->next = next;
    next->prev = last;
}

/**
 * list_splice - join two lists, this is designed for stacks
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice(const struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head, head->next);
}

/**
 * list_splice_tail - join two lists, each list being a queue
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice_tail(struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
}

/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
                    struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head, head->next);
        INIT_LIST_HEAD(list);
    }
}

/**
 * list_splice_tail_init - join two lists and reinitialise the emptied list
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * Each of the lists is a queue.
 * The list at @list is reinitialised
 */
static inline void list_splice_tail_init(struct list_head *list,
                     struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head->prev, head);
        INIT_LIST_HEAD(list);
    }
}

链表初始状态:

img

first->prev = prev;

prev->next = first;

这里prev即head节点

img

last->next = next;

next->prev = last;

这里next即node1节点

img

INIT_LIST_HEAD(list);

最后一步,把list节点重新初始化为头节点,使其前驱后继指针指向自身。

img

上述图示描述了list_splice_init的链表合并过程,函数的作用是把list链表(除list节点自身)插入到head节点后(即head和head->next之间),并重新初始化list节点;

list_splice_tail_init则是与list_splice_init的区别仅是插入的位置不同,其是插入到head节点之前(即head->prev和head之间)。

posted @ 2020-01-05 19:40  流翎  阅读(353)  评论(0编辑  收藏  举报