struct list_head { struct list_head *next, *prev; };
一. 创建链表
#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { WRITE_ONCE(list->next, list); list->prev = list; }
struct list_head mylist = {&mylist, &mylist} ;
struct my_task_list { int val ; struct list_head mylist; }
创建第一个节点
struct my_task_list first_task = { .val = 1, .mylist = LIST_HEAD_INIT(first_task.mylist) };
这样mylist 就prev 和 next指针分别指向mylist自己了,如下图:
二. 添加节点
内核已经提供了添加节点的接口了
/** * 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再调用__list_add接口
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ 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); }
其实就是在head 链表头后和链表头后第一个节点之间插入一个新节点。然后这个新的节点就变成了链表头后的第一个节点了。
依然用上面的my_task_list结构体举例子
LIST_HEAD(header_task);
然后再创建实际的第一个节点
struct my_task_list my_first_task = { .val = 1, .mylist = LIST_HEAD_INIT(my_first_task.mylist) };
接着把这个节点插入到header_task之后
list_add(&my_first_task.mylist, &header_task);
然后在创建第二个节点,同样把它插入到header_task之后
struct my_task_list my_second_task = { .val = 2, .mylist = LIST_HEAD_INIT(my_second_task.mylist) };
其实还可以用另外一个接口 INIT_LIST_HEAD 进行初始化(参数为指针变量), 如下:struct my_task_list my_second_task; my_second_task.val = 2; INIT_LIST_HEAD(&my_second_task.mylist);
list_add(&my_second_task.mylist, &header_task)
以此类推,每次插入一个新节点,都是紧靠着header节点,而之前插入的节点依次排序靠后,那最后一个节点则是第一次插入header后的那个节点。最终可得出:先来的节点靠后,而后来的节点靠前,“先进后出,后进先出”。所以此种结构类似于 stack“堆栈”, 而 header_task就类似于内核stack中的栈顶指针esp, 它都是紧靠着最后push到栈的元素。
上面所讲的list_add接口是从链表头header后添加的节点。 同样,内核也提供了从链表尾处向前添加节点的接口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); }
从注释可得出:(1)在一个特定的链表头前面插入一个节点
(2)这个方法很适用于队列的实现 (why?)
进一步把__list_add ()展开如下:
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ 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); }
(1)创建一个 链表头(实际上应该是表尾), 同样可调用 LIST_HEAD(header_task);
依此类推,每次插入的新节点都是紧挨着 header_task表尾,而插入的第一个节点my_first_task排在了第一位,my_second_task排在了第二位,可得出:先插入的节点排在前面,后插入的节点排在后面,“先进先出,后进后出”,这不正是队列的特点吗(First in First out)!
三. 删除节点
static inline void list_del(struct list_head *entry) { __list_del_entry(entry); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; }
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; WRITE_ONCE(prev->next, next); } /** * 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_entry(struct list_head *entry) { if (!__list_del_entry_valid(entry)) return; __list_del(entry->prev, entry->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; 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)
五. 宿主结构
struct list_head { struct list_head *next, *prev; };
struct my_task_list { int val ; struct list_head mylist; }
那我们如何根据mylist这个字段的地址而找到宿主结构my_task_list的位置呢???container_of(ptr, type, member)
/** * 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)
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
而offsetof定义在/include/linux/stddef.h如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
看下container_of宏的注释:
(1)根据结构体重的一个成员变量地址导出包含这个成员变量mem的struct地址。
(2)参数解释:
struct my_task_list { int val ; struct list_head mylist; }
struct my_task_list first_task = { .val = 1, .mylist = LIST_HEAD_INIT(first_task.mylist) };
而container_of宏的功能就是根据 first_task.mylist字段的地址得出first_task结构的其实地址。
把上面offsetof的宏定义代入container_of宏中,可得到下面定义:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - ((size_t) &((type *)0)->member) );})
再把宏中对应的参数替换成实参:
const typeof( ((struct my_task_list *)0)->mylist ) *__mptr = (&first_task.mylist); \ (struct my_task_list *)( (char *)__mptr - ((size_t) &((struct my_task_list *)0)->mylist) );})
GNU对C新增的一个扩展关键字,用于获取一个对象的类型,比如这里((struct my_task_list *)0)->mylist 是把0地址强制转换成struct my_task_list 指针类型,然后取出mylist元素。 然后再对mylist元素做typeof操作,其实就是获取 my_task_list结构中mylist字段的数据类型struct list_head,所以这行语句最后转化为:
第二条语句中在用 __mptr这个指针 减去 mylist字段在 my_task_list中的偏移(把0地址强制转换成struct my_task_list指针类型,然后取出mylist的地址,此时mylist的地址也是相对于0地址的偏移,所以就是mylist字段相对于宿主结构类型struct my_task_list的偏移) 正好就是宿主结构的起始地址。C语言的灵活性得到了很好的展示!!!
2. 宿主结构的遍历
/** * 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))
/** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) /** * list_next_entry - get the next element in list * @pos: the type * to cursor * @member: the name of the list_head within the struct. */ #define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member)
最终实现了宿主结构的遍历
#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))
struct my_task_list *pos_ptr = NULL ; list_for_each_entry (pos_ptr, & header_task, mylist ) { printk ("val = %d\n" , pos_ptr->val); }
参考文档:https://kernelnewbies.org/FAQ/LinkedLists