赞
踩
Linux内核中需要经常用到双链表,该链表只有指针域,没有数据域。在很多的数据结构中都会嵌入struct list_head结构体变量,它可以使结构体加入到一个双向链表中。链表的初始化,增加,删除等操作的接口在linux-x.x.x/include/linux/list.h里面,内核链表在内核中使用的是如此广泛,所以需要深刻的理解。
1. 基本数据结构
- struct list_head {
- struct list_head *next, *prev;
- };
从结构体成员看,内核链表是一个双向链表,没有数据域,next指向下一个链表元素,prev指向上一个链表元素。
2. 链表的初始化
- #define LIST_HEAD_INIT(name) { &(name), &(name) }
-
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
链表声明和初始化的通常用LIST_HEAD(name)处理。LIST_HEAD定义了一个变量struct list变量name,并且name的next和prev指针都指向name的地址。如下图所示
3. 链表元素添加
- static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
- {
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = 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_tail(struct list_head *new, struct list_head *head)
- {
- __list_add(new, head->prev, head);
- }
__list_add函数的作用是在prev和next指针指向的元素之间插入new元素。如下图所示。
list_add函数向__list_add函数传入的prev和next参数分别是head和head->next,所以list_add函数是在链表头指针和链表第一个元素之间插入new元素。
list_add_tail函数向__list_add函数传入的prev和next指针分别是head->prev和head,head->prev就是指向链表的最后一个元素,所以list_add_tail是在链表最后一个元素和链表头指针之间插入元素。
4. 链表元素删除
- static inline void __list_del(struct list_head * prev, struct list_head * next)
- {
- next->prev = prev;
- prev->next = next;
- }
-
- static inline void __list_del_entry(struct list_head *entry)
- {
- __list_del(entry->prev, entry->next);
- }
-
- static inline void list_del(struct list_head *entry)
- {
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
- }
_list_del函数的作用是删除prev和next指针指向的元素之间的元素,如下图所示
__list_del_entry函数向__list_del函数传入的的prev和next参数分别指向是要删除元素的前一个元素和后一个元素。
list_del函数和__list_del_entry函数的区别是将删除元素的前向指针和后向指针都指向一个非法值,如元素从链表中删除后仍然反问,则会产生内存页错误。
5. 链表的遍历
- /**
- * 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; pos != (head); pos = pos->next)
-
- /**
- * list_for_each_prev - iterate over a list backwards
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- */
- #define list_for_each_prev(pos, head) \
- for (pos = (head)->prev; pos != (head); pos = pos->prev)
-
-
- /**
- * 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; pos != (head); \
- pos = n, n = pos->next)
-
- /**
- * list_for_each_prev_safe - iterate over a list backwards 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_prev_safe(pos, n, head) \
- for (pos = (head)->prev, n = pos->prev; \
- pos != (head); \
- pos = n, n = pos->prev)
-
- /**
- * 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_struct 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))
list_for_each函数是按照从前往后的顺序遍历链表,通过不断指向元素的next元素,直到元素的指针和链表头指针地址相同,则表示链表遍历完成。
list_for_each_prev函数则是从链表的尾部元素向前遍历。
list_for_each_safe函数引入了指针n,用于存储pos的下一个元素的地址。引入指针n可以方便在遍历链表的时候删除pos指向的元素,而不影响遍历。list_for_each无法做到这一点。
list_for_each_prev_safe函数和list_for_each_safe函数的区别是从后往前遍历。
list_for_each_entry函数是list_for_each和list_entry的结合,有pos,head,member三个参数,pos是一个中间变量,指向当前访问的链表元素,head指链表头,member指pos指向的结构体中链表成员变量的名称,示意图如下:
6. 查找链表元素
- /**
- * 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_struct within the struct.
- */
- #define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
list_entry宏有三个参数ptr,type,member。ptr是指数据结构中struct list_head变量成员的地址,type是指数据结构的类型,member是指数据结构中struct list_head的变量名。list_entry宏的结果是ptr指向的type类型的数据结构的变量地址。
示意图如下:
代码示例:
- struct task_struct {
- ...
- struct list_head tasks;
- ...
- };
如果要获取某个struct task_struct类型的变量的地址,ptr指向该变量的tasks指针变量的地址,则可以这么写
struct task_struct *addr = list_entry(ptr, struct task_struct, tasks);
举例:list_for_each和list_entry配合使用
list_for_each函数用于获取数据结构中struct list_head成员的地址,list_entry则是通过struct list_head成员的地址获取到数据结构的地址。
代码示例:
- list_for_each(pos, &list)
- {
- struct task_struct *addr = list_entry(pos, struct task_struct, tasks);
- }
代码示例:
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/rculist.h>
-
- struct stu_info
- {
- char* name;
- int age;
- void (*print)(struct stu_info *stu);
- struct list_head list;
- };
-
- void print_info(struct stu_info *stu)
- {
- printk("name = %s, age = %d\n", stu->name, stu->age);
- }
-
- struct stu_info students[8] = {
- {"Zhao", 20, print_info},
- {"Qian", 21, print_info},
- {"Sun", 22, print_info},
- {"Li", 23, print_info},
- };
-
- LIST_HEAD(stu);
-
- static int student_list_init(void)
- {
- struct list_head *pos;
- struct stu_info *s;
-
- list_add(&students[0].list, &stu);
- list_add(&students[1].list, &stu);
- list_add(&students[2].list, &stu);
- list_add(&students[3].list, &stu);
-
- list_for_each(pos, &stu)
- {
- s = list_entry(pos, struct stu_info, list);
- s->print(s);
- }
-
-
- return 0;
- }
-
- static void student_list_exit(void)
- {
- printk("student_list_exit\n");
-
- return;
- }
-
- module_init(student_list_init);
- module_exit(student_list_exit);
- MODULE_LICENSE("Dual BSD/GPL");
输出结果:
- [19764.110000] name = Li, age = 23
- [19764.120000] name = Sun, age = 22
- [19764.120000] name = Qian, age = 21
- [19764.130000] name = Zhao, age = 20
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。