Linux 内核里有一类常见的操作:遍历由 list_head 串起来的结构体链表。list_for_each_entry 就是干这个事儿的。它藏在 include/linux/list.h,是一个宏,展开后是一个 for 循环。
/**
* 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))
参数很直观:pos 是循环游标,类型和你链表的节点类型一致;head 是链表头指针;member 是节点结构体里嵌的那个 list_head 字段的名字。用法上,只要你有一个以 LIST_HEAD(my_list) 定义的链表头,并且通过 list_add_tail 把节点加进去,就可以写:
struct my_struct {
int data;
struct list_head node;
};
struct my_struct *p;
list_for_each_entry(p, &my_list, node) {
// 在这里访问 p->data
}
实际内核代码里到处是这个模式。比如 input 子系统遍历 handler 链表:
struct input_handler {
void *private;
...
const char *name;
const struct input_device_id *id_table;
struct list_head h_list;
struct list_head node;
};
struct input_handle *handle;
static LIST_HEAD(input_handler_list);
// 先前用 list_add_tail(&dev->node, &input_handler_list) 加入链表
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
或者 RC map 的注册和查找:
struct rc_map_list {
struct list_head list;
struct rc_map map;
};
static LIST_HEAD(rc_map_list);
static struct rc_map_list *seek_rc_map(const char *name)
{
struct rc_map_list *map = NULL;
spin_lock(&rc_map_lock);
list_for_each_entry(map, &rc_map_list, list) {
if (!strcmp(name, map->map.name)) {
spin_unlock(&rc_map_lock);
return map;
}
}
spin_unlock(&rc_map_lock);
return NULL;
}
int rc_map_register(struct rc_map_list *map)
{
spin_lock(&rc_map_lock);
list_add_tail(&map->list, &rc_map_list);
spin_unlock(&rc_map_lock);
return 0;
}
static struct rc_map_list encore_enltv_map = {
.map = {
.scan = encore_enltv,
.size = ARRAY_SIZE(encore_enltv),
.rc_type = RC_TYPE_UNKNOWN,
.name = RC_MAP_ENCORE_ENLTV,
}
};
static int __init init_rc_map_encore_enltv(void)
{
return rc_map_register(&encore_enltv_map);
}
要想明白这个宏是怎么工作的,得看它依赖的几个助手宏:
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
整个链条归结到 container_of。
// ./kernel-3.18/include/linux/kernel.h
#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)
container_of 的巧妙之处在于:它知道结构体里某个成员的地址 ptr,反推出整个结构体的起始地址。做法是先用 typeof(((type *)0)->member) 拿到成员的类型,将 ptr 赋给一个同类型的局部指针 __mptr,然后用 offsetof(type, member) 算出该成员在结构体里的偏移量,最后用 __mptr 减去这个偏移量,就得到了结构体指针。
offsetof 的实现很经典:把 0 强制类型转换为 (type*),那么成员 MEMBER 的地址就是它在结构体内的偏移。整个运算在编译时就能确定,没有运行时开销。
回到 list_for_each_entry:
- 初始化:
pos = list_first_entry(head, typeof(*pos), member)展开后是container_of(head->next, typeof(*pos), member),也就是拿到链表头下一个元素的宿主结构体指针。注意,链表头本身不参与遍历,它的next指向第一个实际节点。 - 条件判断:
&pos->member != (head)—— 当遍历指针的member地址等于链表头时,说明绕了一圈,循环结束。 - 步进:
pos = list_next_entry(pos, member)展开成container_of(pos->member.next, typeof(*pos), member),从当前节点的member.next获取下一个节点的宿主结构体指针。
整个过程干净利落,完全基于编译时类型推导,没有对 void* 的显式转换,类型安全由编译器保证。
实际使用中有两点需要留意:第一,必须在链表头开始遍历,如果你从一个中间节点开始,条件 &pos->member != head 可能永远不成立,导致死循环。第二,list_for_each_entry 不保护遍历过程中的删除操作,如果需要安全删除,得用 list_for_each_entry_safe 这样的变体。
理解了这个宏,再看内核里的各种链表操作——设备链表、定时器链表、文件系统 inode 链表——就都一个套路了。

