内核链表
1 list_head 结构
为了使用链表机制,驱动程序需要包含<linux/types.h>头文件,该文件定义了如下结构体实现双向链:
struct list_head {
struct list_head *next, *prev;
};
2 链表的初始化
2.1 链表宏定义和初始化
可使用以下宏定义并初始化一个链表头部list_head,list_head 不包含数据部分。LIST_HEAD_INIT将链表头的 next 和 prev 指针都指向链表头部,从而形成一个循环结构,和下面介绍的INIT_LIST_HEAD函数一样。
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
2.2 链表的初始化
INIT_LIST_HEAD 是一个用于初始化链表头的函数,它将链表头的 next 和 prev 指针都指向自己,从而形成一个循环结构。
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
WRITE_ONCE(list->prev, list);
}
如下图所示,链表头的 next 和 prev 指针都指向自己。
3 list_add
在链表的头部添加新链表项,以下是实现:
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);
}
以下为添加示意图,可以看出后添加节点放在链表的头部,先添加节点靠后,先进后出,后进先出,类似栈结构。
4 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);
}
以下为添加示意图,可以看出新添加节点放在链表的尾部,后添加节点靠,先进先出,后进后出,类似FIFO结构。
5 遍历节点
5.1 list_entry
list_entry 宏通过调用 container_of 宏,从链表节点指针获取包含该节点的结构体指针。
/**
* list_entry - get the struct for this entry
* @ptr: 指向 &struct list_head 的指针。
* @type: 包含该节点的结构体类型。
* @member: 结构体中的 list_struct 名称。
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
5.2 list_for_each
list_for_each 从链表的头部往后依次遍历(next方向)。
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next)
5.3 list_for_each_entry
通过for循环,依次遍历链表中的每个节点,next方向遍历,每个节点的宿主为pos。
/**
* 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); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
list_first_entry 宏:
#define list_first_entry(ptr, type, member) \
container_of((ptr)->next, type, member)
list_first_entry 宏用于获取链表的第一个节点的结构体指针。通过 (ptr)->next 获取到链表头部之后的第一个节点的指针,然后通过 container_of 宏获取包含该节点的整个结构体指针。
list_entry_is_head 宏:
#define list_entry_is_head(pos, head, member) \
((pos)->member == (head))
list_entry_is_head 宏用于检查当前节点是否是链表的头部。比较 pos->member 是否等于 head,如果相等,则说明当前节点是链表的头部,即遍历结束。
list_next_entry 宏:
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
list_next_entry 宏用于获取下一个节点的结构体指针。通过 (pos)->member.next 获取到当前节点的下一个节点的指针,然后通过 list_entry 宏获取包含该节点的整个结构体指针。