linux kernel基础双向链表定义及基本操作
1. 数据结构定义
struct list_head {
struct list_head *next, *prev;
};
2. 操作
2.1 定义一个双向链表
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
2.2 初始化一个双向链表
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
2.2.1 图示
- 空链表,只有一个HEAD节点
2.3 头部插入新节点
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
说明:
- list_add节点本质上是在指定节点之后添加新节点
- 所以,当指定节点为HEAD节点时,即为在链表头部插入,因为新插入的节点为HEAD之后的第一个节点
2.3.1 图示
- 头节点HEAD
- 待插入节点NEW
- 执行插入操作之前head,head->next都为HEAD节点本身
第一次插入:
执行插入操作后,整理可得
第二次插入:
- 头节点HEAD,第一次插入节点NEW
- 待插入节点NEW2
- 执行插入操作之前head为HEAD节点,head->next为第一次插入的NEW节点
执行第二次节点插入操作
- 上图中省略了在本次插入过程中未被破坏的HEAD节点和NEW节点之间的prev/next的连接关系,即:
- head.prev = NEW;
- new.next = HEAD
2.4 尾部插入新节点
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
说明:
- list_add_tail本质上是在指定节点之前插入新节点
- 所以当指定节点为HEAD节点时,为往链表尾部插入新节点,因为插入的节点时钟为HEAD节点的prev节点
- 尾插法的第一次插入操作与头插法相同,第一次的节点插入操作,无论函数逻辑怎么设计,链表中始终是一个HEAD节点与一个NEW节点,且保持首尾互相连接。
- 第二次插入时,不同于头插法,新插入NEW2节点的prev(函数传参中head.prev)为第一次插入的NEW节点,next(函数传参中的head)为HEAD节点
2.4.1 图示
第二次插入前状态:
第二次插入结果:
- 同样的,上图中省略了,本次插入过程中未被破坏的HEAD节点和NEW节点之间的连接关系,
- 可以将上图重新整理成以下形式:
2.5 删除节点
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
2.6 判断节点是否是链表的尾节点
- 链表的尾节点指向HEAD节点
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
2.7 判断链表是否为空
- 空链表中仅余一个HEAD节点
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
2.8 判断链表是否为单节点
- 链表非空,且prev指针等于next指针
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
2.9 遍历链表
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
2.10 反向遍历链表
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
2.11 安全遍历链表
- 提前保存了next节点地址i,可用于轮询时删除节点,
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)