Linux 内核链表
1.内核链表的介绍
内核已经实现了链表的相关操作的api,我们不需要再重复去自定义自己的链表,最好了解内核链表的api函数原理并且进行使用
内核链表是使用环形双链表的形式,看下图
通常我们存储一个数据结构到链表中的方法是在结构体中嵌入一个链表指针,如下
struc data_demo {
int year;
int month;
int days;
struct data_demo *prev;
struct data_demo *next;
}List;
Linux 内核方式与通常不一样,他不是将数据结构塞入链表,而是将链表节点塞入数据结构
struct list_head {
struct list_head *prev;
struct list_head *next;
}
struct data_demo {
int year;
int month;
int days;
struct list_head list;
}List;
2.内核链表的使用
1.初始化链表头
由于结构体内嵌list_head指针,我么可以从任何一个节点开始遍历链表,不过也是需要一个特殊的指针索引到整个链表,特殊的
索引节点就是一个常规的list_head
创建索引list_head
struct list_head demo_list = LIST_HEAD_INIT(demo_list);
LIST_HEAD(demo_list);
//两个宏定义都可以使用
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
2.插入新的节点
把新的节点插入到链表中有两个方法list_add和list_add_tail
1.list_add
//head 为索引节点 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(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);
}
这个就类似头插法,每次都把新的节点都插入到索引节点之后,如图
先是创建的node1先插入到索引节点demo_lsit后
Demo_list->next->prev = node1; //demo_list->next 就是指向demo_list本身 然后再用demo_list->prev = node1
node1->next = Demo_list->next; //
node1->prev = Demo_list;
Demo_list->next = node1;
然后再创建node2插入到链表中
Demo_list->next->prev = node2; //把node1的prev节点指向node2
node2->next = Demo_list->next; //把Demo_list的next指向的节点赋值给node2->next
node2->prev = Demo_list; //node2 prev指针指向Demo_list
Demo_list->next = node2; //Demo_list next节点再指向node2
2.list_add_tail
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
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);
}
带入__list_add函数
head->prev = new;
new->next = head;
new->prev = head->prev;
head->prev->next = new;
这个就类似与尾插法,把索引节点当做尾结点,每次都在尾结点之前插入节点
先是创建node1插入到demo_list之前
Demo_list->prev = node1; //demo_list prev指针指向node1
node1->next = Demo_list; /
node1->prev = Demo_list->prev; //这边有个疑问 node1->prev = Demo_list->prev 不就是node1->prev = node1吗,后续再看
Demo_list->prev->next = node1;
创建node2插入到链表中
Demo_list->prev = node2;
node2->next = Demo_list;
node2->prev = Demo_list->prev;
Demo_list->prev->next = node2;
3.遍历链表
1.list_for_each_entry
正向遍历链表
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
eg:
struct demo_list *entry;
list_for_each_entry(entry, demo_list, list)
2.list_for_each_entry_reverse
反向遍历链表
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
4.删除链表节点
1.list_del
只是把节点从链表中删除掉,并不释放内存
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
2.list_del_init
只是把节点从链表中删除掉,并不释放内存,再次初始化entry能够使用改数据结构
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
5.移动和合并链表节点
1.list_move
从一个链表删除一个节点,并且按头插法的方式插入到另外一个链表
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
2.list_move_tail
从一个链表删除一个节点,并且按尾插法的方式插入到另外一个链表
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
6.判断指定链表是否为空
1.list_empty
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
tail(list, head);
}
### 6.判断指定链表是否为空
#### 1.list_empty
```c
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}