【kernel】——宏 list_entry/container_of

本文介绍了C语言中用于结构体操作的宏`list_entry`和`container_of`,详细解释了它们的作用、原型,并通过`offsetof`、`typeof`等预备知识进行了深入解析,展示如何通过结构体成员指针获取整个结构体的地址。

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

作用

宏 list_entrycontainer_of 用来通过结构体的成员指针来返回整个结构体的地址。

  • 第一个参数:ptr 是结构体中某个成员的地址
  • 第二个参数:type 是结构体类型
  • 第三个参数:member 是该结构体中成员的名字

原型

#define list_entry(ptr, type, member) \
        container_of(ptr, type, member)

#define container_of(ptr, type, member) ({    \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

解析

预备知识,需要解析这个宏我们需要有以下预备知识:

首先来分析 offsetof 宏。它的作用是用于计算 TYPE 结构体中成员 MEMBER 的偏移量。

测试程序如下:

#include <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#defin
<think>好的,用户想了解Linux内核中的list_for_each_entry的解析与使用方法。我需要先回忆一下相关的知识,并参考提供的引用资料来构建答案。 首先,根据引用[2]和[4],Linux内核链表使用list_head结构体实现,这样可以实现链表与数据结构的解耦。list_for_each_entry是用来遍历包含list_head的结构体的。 接下来,我需要解释这个的工作原理。根据引用[2],container_of是关键,它可以通过结构体成员的地址找到整个结构体的起始地址。list_for_each_entry应该就是利用这个机制来遍历链表的。 然后,需要给出的定义。引用[2]提到list_for_each_entrylist_for_each_entry_safe,这里用户问的是前者。根据资料,的参数包括结构体指针、链表头、以及结构体中list_head成员的名称。比如list_for_each_entry(pos, head, member)。 使用步骤方面,应该先初始化链表头,如引用[3]中的INIT_LIST_HEAD函数。然后添加节点,比如使用list_add_tail(引用[5])。遍历时用list_for_each_entry,每次获取包含list_head的结构体实例。 示例代码部分,需要构造一个包含list_head的结构体,比如struct my_data。然后展示如何初始化链表,添加节点,并遍历打印数据。注意代码中的安全性和正确性,比如使用list_add_tail添加到链表尾部。 还要注意线程安全问题,引用[2]提到list_for_each_entry_safe用于可能删除节点的情况,这里需要说明两者的区别。 最后,常见问题解答部分,解释container_of的作用,的参数含义,以及遍历时是否需要加锁,这样用户能更全面地理解使用场景和注意事项。 检查是否有遗漏的点,比如链表初始化的方法、添加节点的函数,以及遍历过程中如何获取结构体成员。确保引用的资料正确支持这些内容,比如引用[4]说明了解耦的设计,引用[5]展示了初始化函数。 现在整理这些内容,用清晰的结构分点解析,并给出示例代码,确保用户能够理解并正确使用该。</think>### list_for_each_entry 解析与使用 #### 一、定义与作用 `list_for_eeach_entry` 是 Linux 内核中用于遍历包含链表节点(`struct list_head`)的结构体的。其核心原理是通过 `container_of` [^2] 从链表节点反向定位到宿主结构体的起始地址,实现链表操作与数据结构的解耦[^4]。 原型定义: ```c #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)) ``` 参数说明: - `pos`:指向宿主结构体的指针,用于迭代 - `head`:链表头节点指针 - `member`:宿主结构体中 `list_head` 成员的名称 #### 二、使用步骤 1. **初始化链表头** 使用 `INIT_LIST_HEAD` 初始化链表头节点[^3][^5]: ```c struct list_head my_list; INIT_LIST_HEAD(&my_list); ``` 2. **添加节点到链表** 通过 `list_add_tail` 添加节点到链表尾部: ```c struct my_data { int value; struct list_head list; }; struct my_data *node = kmalloc(sizeof(*node), GFP_KERNEL); node->value = 42; list_add_tail(&node->list, &my_list); // 添加到链表 ``` 3. **遍历链表** 使用遍历链表并访问数据: ```c struct my_data *pos; list_for_each_entry(pos, &my_list, list) { printk(KERN_INFO "Value: %d\n", pos->value); } ``` #### 三、关键机制解析 1. **container_of ** 通过成员地址计算宿主结构体地址: ```c #define container_of(ptr, type, member) \ (type *)((char *)(ptr) - offsetof(type, member)) ``` 例如:`list_entry(node, struct my_data, list)` 返回宿主结构体指针[^2] 2. **安全遍历变体** 使用 `list_for_each_entry_safe` 支持遍历时删除节点: ```c struct my_data *pos, *n; list_for_each_entry_safe(pos, n, &my_list, list) { if (pos->value == 42) { list_del(&pos->list); kfree(pos); } } ``` #### 四、典型应用场景 - 内核模块管理(如设备驱动链表) - 进程调度队列 - 文件系统对象管理 #### 代码示例 ```c // 定义数据结构 struct task_info { pid_t pid; char comm[16]; struct list_head list; }; // 初始化链表 LIST_HEAD(task_list); // 添加任务 struct task_info *task1 = kmalloc(sizeof(*task1), GFP_KERNEL); task1->pid = 1001; strcpy(task1->comm, "init"); INIT_LIST_HEAD(&task1->list); list_add_tail(&task1->list, &task_list); // 遍历输出 struct task_info *pos; list_for_each_entry(pos, &task_list, list) { printk("PID: %d, Command: %s\n", pos->pid, pos->comm); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tyustli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值