list_for_each(pos, head)、list_for_each_entry(pos, head, member)

一、list_head

Linux 内核定义了 list_head 数据结构,字段 next 和 prev 分别表示通用双向链表向前和向后的指针元素。不过,值得特别关注的是,list_head 字段的指针中存放的是另一个 list_head 字段的地址,而不是含有 list_head 结构的整个数据结构地址
用 list_head 数据结构构造的一个双向链表如下所示:


list_for_each(pos, head)、list_for_each_entry(pos, head, member)_第1张图片
这里写图片描述

二、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 的关系,如下图所示:

list_for_each(pos, head)、list_for_each_entry(pos, head, member)_第2张图片
这里写图片描述

由上图可以看出:

  • 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 结构的数据结构的遍历的

你可能感兴趣的:(list_for_each(pos, head)、list_for_each_entry(pos, head, member))