本文构建了一个双向循环的内核链表,然后对链表进行遍历并打印了数据,最后释放了链表节点。使用到的数据结构和链表操作函数如下:
struct list_head 内核提供的双向循环链表节点的结构体
LIST_HEAD(name) 该宏定义并初始化一个名为name的struct list_head类型节点
INIT_LIST_HEAD(name) 该宏初始化一个由name指向的 struct list_head类型节点,事先需要定义好一个struct list_head类型变量,
并将变量的指针赋给name,然后再使用该宏
list_for_each(pos, head) 该宏可以遍历以head为链表头的循环链表,pos是遍历到的每个节点,pos和head均为指针类型。
list_entry(ptr, type, number) 该宏可以得到type类型的结构体指针,number为包含在该type类型结构体中的struct list_head类型成员变量,
ptr为&number。返回值为指向type类型的指针。
list_for_each_safe(pos, n, head)该宏类似于list_for_each宏,区别在于每次遍历,n指向了pos的下一个节点。该宏可用于释放链表节点之用。
程序代码如下:
1 #include <linux/slab.h> 2 #include <linux/sched.h> 3 #include <linux/module.h> 4 #include <linux/kernel.h> 5 #include <linux/init.h> 6 #include <linux/list.h> 7 8 struct fox { 9 int data; 10 struct list_head list; 11 }; 12 13 int i; 14 struct list_head *temp; 15 struct fox *tmp; 16 LIST_HEAD(head); 17 18 static int __init test_init(void) 19 { 20 for (i = 0; i < 10; i++) { 21 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); 22 tmp->data = i; 23 INIT_LIST_HEAD(&tmp->list); 24 list_add_tail(&tmp->list, &head); 25 } 26 27 list_for_each(temp, &head) { 28 tmp = list_entry(temp, struct fox, list); 29 printk("<0> %d\n", tmp->data); 30 } 31 32 return 0; 33 } 34 35 static void __exit test_exit(void) 36 { 37 struct list_head *next; 38 39 printk("<1> byebye\n"); 40 list_for_each_safe(temp, next, &head) { 41 tmp = list_entry(temp, struct fox, list); 42 printk("<0> %d\n", tmp->data); 43 kfree(tmp); 44 } 45 } 46 47 module_init(test_init); 48 module_exit(test_exit); 49 MODULE_LICENSE("GPL");
需要注意的是,在释放链表时,不可以直接用list_del(pos)宏来删除节点,该宏仅仅是把struct list_head节点从其链表中卸下来,而且不释放。而我们需要删除的是fox结构体,所以只能使用本文中的这种方法,利用list_for_each_safe()宏,将需要释放的节点指针保存到pos中,同时将下一个节点指针保存在next中,这样就保证了释放节点时链表不会丢失。其实list_head节点是不用单独去释放的,该结构体一般会以结构体变量的形式保存在更大的结构体中,只要释放更大结构题即可。如本例所示的那样。
内核链表的应用