list_for_each_entry(handler, &input_handler_list, node)的理解

在研究Linux input子系统时,遇到list_for_each_entry宏的使用,该宏用于遍历handler链表。文章解析了在input_register_device()函数中调用此宏的含义,指出它不是函数而是宏定义,且没有分号。通过实例化宏,解释了其作用,帮助新手理解这一常见困惑。虽然未深入讲解container_of等细节,但旨在提供整体理解,避免在学习过程中迷失方向。

近期,作为一个新手在研究input子系统的过程中,发现这么一句话list_for_each_entry(handler, &input_handler_list, node),当时也是在听一些课程,在input_register_device()函数中调用了list_for_each_entry(handler, &input_handler_list, node),说此处为遍历handler链表进行匹配,如下所示。

int input_register_device(struct input_dev *dev)
{
        struct input_handler *handler;
        ...
        //加入链表尾部
	    list_add_tail(&dev->node, &input_dev_list);
	    //遍历handler链表进行匹配
	    list_for_each_entry(handler, &input_handler_list, node)
		    input_attach_handler(dev, handler);
        ...

}

其中卡在了对于下属函数的分析:

list_for_each_entry(handler, &input_handler_list, node)

上述是个函数这句话就是错误的!,所以一开始一直分析不出handler这个局部变量一直没有赋值,那这个传参有什么意义呢?说是对链表进行遍历,然后遍历了一遍赋给handler的是什么呢?最后一个元素?那显然不合理呀!经过层层分析,网上查看blog,在一些小地方恍然大悟!

因为上一句话是一个宏,而且其最后是没有分号的!

那么现在将这个宏

<think>我们被要求解释list_for_each_entry函数,根据提供的引用(特别是引用[4]),该函数是Linux内核中用于遍历链表的一个宏。 函数定义(来自引用[4]): #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) 参数说明: - pos: 指向当前链表节点所在结构体的指针,在循环中它会依次指向链表中的每个元素。 - head: 链表头指针(注意链表头通常是一个独立的节点,不包含有效数据,用于标记链表的开始和结束)。 - member: 链表节点(struct list_head)在包含它的结构体中的成员名称。 功能:遍历链表,每次循环将pos指向链表节点所在的结构体(即包含链表节点的父结构体)。 具体步骤: 1. 初始化:将pos初始化为链表头后面第一个节点所嵌入的结构体。 通过list_entry((head)->next, typeof(*pos), member)实现。 2. 循环条件:检查当前节点的member成员的地址是否不等于链表头head(即还没有遍历完一圈)。 3. 迭代:将pos更新为下一个节点所嵌入的结构体。 其中,list_entry宏的作用是通过结构体中的成员(member)的指针来获取整个结构体的指针。它通常使用container_of宏实现。 示例(引用[2]): list_for_each_entry(handler, &input_handler_list, node) { ... // 这里handler就是遍历的每个结构体,node是结构体中的list_head成员名。 } 注意事项: - 链表头head必须是一个已经初始化的链表头(通常用INIT_LIST_HEAD初始化)。 - 链表头不包含有效数据,所以遍历从head->next开始。 - 在遍历过程中,不能删除当前节点(除非使用安全版本list_for_each_entry_safe)。 另外,引用[3]提到,list_for_each_entry_safe是另一个宏,它使用一个临时变量来保存下一个节点,从而允许在遍历过程中删除当前节点。 因此,我们可以这样解释: </think> ### `list_for_each_entry` 函数详解 `list_for_each_entry` 是 Linux 内核中广泛使用的链表遍历宏,用于高效遍历**包含链表节点的结构体**。它通过链表头指针自动遍历所有节点,并返回包含该节点的父结构体指针。 #### 1. 函数定义(基于引用[4]) ```c #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) ``` #### 2. 参数解析 | 参数 | 类型 | 说明 | |---------|--------------|----------------------------------------------------------------------| | `pos` | 结构体指针 | 当前遍历到的结构体指针,每次迭代自动指向下一个元素 | | `head` | `struct list_head*` | 链表头指针(指向哨兵节点,不存储实际数据) | | `member`| 成员名 | 链表节点在结构体中的字段名称(例如 `node`, `list` 等) | #### 3. 工作原理 1. **初始化** 通过 `list_entry((head)->next, ...)` 获取第一个有效节点的父结构体指针 - `(head)->next`:跳过哨兵节点,指向第一个实际节点 - `list_entry`:通过成员变量地址反推父结构体地址(使用 `container_of` 宏) 2. **遍历条件** `&pos->member != (head)`:当节点指针回到链表头时终止循环 3. **迭代步进** `pos->member.next` 获取下一个节点,再通过 `list_entry` 转换为父结构体指针 #### 4. 执行流程示例 ```c struct device { int id; struct list_head list; // 嵌入的链表节点 }; LIST_HEAD(device_list); // 初始化链表头 // 遍历示例 struct device *dev; list_for_each_entry(dev, &device_list, list) { printk("Device ID: %d\n", dev->id); } ``` 1. `dev = 第一个device结构体的地址` 2. 检查 `dev->list` 是否指向链表头 3. 执行循环体 4. `dev = 下一个device结构体地址` 5. 重复步骤2-4直至遍历完成 #### 5. 关键技术支持 - **`list_entry` 宏**(引用[1][4]) 通过成员变量地址计算父结构体地址: ```c #define list_entry(ptr, type, member) \ container_of(ptr, type, member) ``` - **`container_of` 原理**(引用[1]) 利用结构体内存布局计算起始地址: ```c #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) ``` 其中 `offsetof(type, member)` 计算成员在结构体中的偏移量 #### 6. 安全版本:`list_for_each_entry_safe` 当需要在遍历中删除节点时使用(引用[3]): ```c list_for_each_entry_safe(pos, n, head, member) ``` 额外参数 `n` 临时保存下一个节点指针,防止删除当前节点后链表断裂。 #### 7. 典型应用场景 - 设备驱动管理(如 input 子系统遍历处理器,引用[1]) - 内核对象管理(进程、文件描述符等) - 网络协议栈中的数据包队列处理 > 该宏通过**零额外内存开销**实现类型安全的链表遍历,是Linux内核高效设计的核心组件之一[^1][^2][^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值