一、list_head
Linux 内核定义了 list_head 数据结构,字段 next 和 prev 分别表示通用双向链表向前和向后的指针元素。不过,值得特别关注的是,list_head 字段的指针中存放的是另一个 list_head 字段的地址,而不是含有 list_head 结构的整个数据结构地址
用 list_head 数据结构构造的一个双向链表如下所示:
二、list_for_each(pos, head)
list.h
/**
* 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)
由上可知,这个宏是对表头地址 head 指定的链表进行扫描,在每次循环时,通过 pos 返回指向链表元素的 list_head 结构的指针。这个的实现很简单,没有什么需要详细说明的
三、list_for_each_entry(pos, head, member)
list.h
/**
* 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 类似,但是返回包含了 list_head 结构的数据结构的地址,而不是 list_head 结构本身的地址
下面就来详细看一下这是怎么实现的,我们就拿遍历父进程 father 的 children 链表来举例,那么 head 即为 &father->children,member 即为 sibling
3.1 list_first_entry(ptr, type, member)
list.h
/**
* 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_struct 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_entry((&father->children)->next, type, member),所以 for 循环的初始化即为:
pos = list_entry((&father->children)->next, type, member)
3.2 list_entry(ptr, type, member)
list.h
/**
* 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)
kernel.h
/**
* 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) );})
即相当于 pos = container_of((&father->children)->next, type, member),那么第一行即为:
const list_head *__mptr = (&father->children)->next;
第二行即为:
(task_struct *)( (char *)__mptr - offsetof(task_struct, sibling) );
即相当于 __mptr 指向的地址减去 sibling 在 task_struct 中的偏移,再转化为 (task_struct *) 类型的指针
用方程组的思想来理解 container_of 的作用即为:
x->member = ptr
,求 x 是多少,代入例子中即为:
x->sibling = (&father->children)->next
,返回 x
我们再来看一下 task_struct 结构体中 children 与 sibling 的关系,如下图所示:
由上图可以看出:
- Child 的 sibling 指向的地址实际在 Parent 的 children 指向的双向链表中
- 当调用 list_entry((&father->children)->next, type, member) 时,实际就会返回指向 Child1 的指针
3.3 list_next_entry(pos, member)
list.h
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_struct within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
由上可以看出 list_next_entry 也是通过 list_entry 实现的,代入例子就是:
list_entry((Child1)->sibling.next, task_struct, sibling)
也就是返回指向 Child2 的指针
综上所述,list_for_each_entry(pos, head, member) 就是通过这样的方式实现对包含了 list_head 结构的数据结构的遍历的