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

本文详细介绍Linux内核中list_head结构及list_for_each_entry宏的实现原理,展示了如何使用这些工具高效遍历包含特定数据结构的双向链表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值