0x01:链表
链表是Linux内核中非常重要的数据结构,在日常内核驱动开发过程中,往往需要遍历内核的某个链表,内核提供了一套完整的双链表机制,使开发者可以在内核私有数据结构中轻松构建双向链表,一起学习双向链表的构建和遍历方法,具体链表的知识可以参考文末链接;
0x02:数据结构及关键函数
-
链表可以用
list_head
来描述struct list_head { struct list_head *next, *prev; };
list_head结构体不包含链表节点的数据区,而是通常嵌入其他数据结构,如page 数据结构中就嵌入了lru链表节点,通常的做法是将page结构体挂入LRU链表。
struct page { ... struct list_head lru; ... }
-
链表头初始化
- 静态初始化,name为链表头名
#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name)
- 动态初始化
* INIT_LIST_HEAD - Initialize a list_head structure * @list: list_head structure to be initialized. * * Initializes the list_head to point to itself. If it is a list header, * the result is an empty list. */ static inline void INIT_LIST_HEAD(struct list_head *list) { WRITE_ONCE(list->next, list); WRITE_ONCE(list->prev, list); }
-
添加节点到链表
- list_add
把新节点添加到链表头。
/** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }
- list_add_tail
把新节点添加到表尾。
/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }
- list_add
-
从链表中删除某个节点
- list_del
/** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del_entry(entry); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; }
-
替换链表中的某个节点
- list_replace
/** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ 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; }
-
合并链表
list_splice(struct list_head *list,struct list_head *head)
合并两个链表,它将 list 指向的链表的每个节点插入到指定链表的 head 元素后面(不会修改 list 链表本身)。list_splice_init(struct list_head *list,struct list_head *head)
与list_splice函数一致,但是会重新init list,相当与删除原链表。
-
链表遍历
- list_entry
/** * 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_head within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member)
- list_for_each_safe
获取结构体指针需要使用list_entry()
,可以参考示例。
/** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; \ !list_is_head(pos, (head)); \ pos = n, n = pos->next) // 参考使用示例 getframe_deferred(struct aoedev *d, u32 tag) { struct list_head *head, *pos, *nx; struct frame *f; head = &d->rexmitq; list_for_each_safe(pos, nx, head) { f = list_entry(pos, struct frame, head); if (f->tag == tag) { list_del(pos); return f; } } return NULL; }
- 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); \ !list_entry_is_head(pos, head, member); \ pos = list_next_entry(pos, member))
0x03:双向链表使用示例
- 示例代码
//list.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
/* header of list */
#include <linux/list.h>
/* private structure */
struct node {
const char *name;
struct list_head list;
};
/* Initialize a group node structure */
static struct node node0 = { .name = "BiscuitOS_node0", };
static struct node node1 = { .name = "BiscuitOS_node1", };
static struct node node2 = { .name = "BiscuitOS_node2", };
static struct node node3 = { .name = "BiscuitOS_node3", };
static struct node node4 = { .name = "BiscuitOS_node4", };
static struct node node5 = { .name = "BiscuitOS_node5", };
static struct node node6 = { .name = "BiscuitOS_node6", };
/* Declaration and implement a bindirect-list */
LIST_HEAD(BiscuitOS_list);
static __init int list_entry_init(void)
{
struct node *np;
/* add a new entry on special entry */
list_add_tail(&node0.list, &BiscuitOS_list);
list_add_tail(&node1.list, &BiscuitOS_list);
list_add_tail(&node2.list, &BiscuitOS_list);
printk("BiscuitOS_list:\n");
/* Traverser all node on bindirect-list */
list_for_each_entry(np, &BiscuitOS_list, list)
printk("%s\n", np->name);
/* get the struct for this entry */
np = list_entry(BiscuitOS_list.next, struct node, list);
printk("The entry struct node: %s\n", np->name);
/* get the first element from a list */
np = list_first_entry(&BiscuitOS_list, struct node, list);
printk("The first entry struct node: %s\n", np->name);
return -1;
}
static void __exit list_entry_exit(void)
{
printk("Bye bye list_entry\n");
}
module_init(list_entry_init);
module_exit(list_entry_exit);
MODULE_LICENSE("GPL");
Makefile:
obj-m += list.o
KBUILD_CFLAGS+= -g
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- 测试效果
[ 2113.170373] BiscuitOS_list:
[ 2113.170374] BiscuitOS_node0
[ 2113.170374] BiscuitOS_node1
[ 2113.170375] BiscuitOS_node2
[ 2113.170376] The entry struct node: BiscuitOS_node0
[ 2113.170376] The first entry struct node: BiscuitOS_node0