跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

Linux 内核 list_for_each_entry 链表遍历详解

Linux 内核链表遍历宏 list_for_each_entry 基于 container_of 和 offsetof 实现结构体指针转换与节点迭代。该宏通过 list_first_entry 初始化游标,list_next_entry 更新位置,配合 list_entry 完成类型安全访问。适用于遍历包含 list_head 成员的结构体集合,是内核开发中操作双向链表的核心机制。

指针猎手发布于 2026/4/9更新于 2026/9/556 浏览

概述

Linux 内核中常用 list_for_each_entry 来遍历整个实体链表。

下面函数结构:

/**
 * 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_entry 作用:所有包含 list_head 的数据结构,均可使用此方法遍历链表;list_head 结构体不包含数据部分,使用该函数进行遍历链表节点,然后在循环体中,对链表的数据部分进行读写操作,通过对链表中 list 成员的遍历,即可定位到链表的相关节点,进而访问链表节点中的数据部分。

用法

如下面用法:

示例 1:

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_dev_list); //加入链表表
list_for_each_entry(handler, &input_handler_list, node) //遍历
    input_attach_handler(dev, handler); //应用

示例 2:

  {
    u32 scancode;
    u32 keycode;
};


    
      size; 
      len; 
      alloc; 
    
      *name;
     lock;
};


    
    
};

 ;

  rc_map_list *
{
     ;
    spin_lock(&rc_map_lock);
    list_for_each_entry(, &rc_map_list, ) {
        
        
         (!(name, ->.name)) {
            
            spin_unlock(&rc_map_lock);
             ;
        }
    }
    spin_unlock(&rc_map_lock);
     ;
}

 
{
    spin_lock(&rc_map_lock);
    list_add_tail(&->, &rc_map_list); 
    spin_unlock(&rc_map_lock);
     ;
}

  {
    . = {
        .scan = encore_enltv,
        .size = ARRAY_SIZE(encore_enltv),
        .rc_type = RC_TYPE_UNKNOWN, 
        .name = RC_MAP_ENCORE_ENLTV,
    }
};

  __init 
{
     rc_map_register(&encore_enltv_map);
}
struct
rc_map_table
struct rc_map {
struct rc_map_table *scan;
unsigned
int
/* Max number of entries */
unsigned
int
/* Used number of entries */
unsigned
int
/* Size of *scan in bytes */
enum rc_type rc_type;
const
char
spinlock_t
struct rc_map_list {
struct list_head list;
struct rc_map map;
static
LIST_HEAD
(rc_map_list)
static
struct
seek_rc_map
(const char *name)
struct rc_map_list *map =
NULL
map
list
// list: the name of the list_struct within the struct.
// 遍历
if
strcmp
map
map
// 应用
return
map
return
NULL
int
rc_map_register
(struct rc_map_list *map)
map
list
//加入链表表
return
0
static
struct rc_map_list encore_enltv_map =
map
/* Legacy IR type */
static
int
init_rc_map_encore_enltv
(void)
return

代码展示

// ./kernel-3.18/include/linux/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_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_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_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-3.18/include/linux/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) );})

代码分析

从上面可以知道 list_for_each_entry -> list_first_entry -> list_entry -> container_of


list_next_entry -> list_entry -> container_of

4.1 分析 container_of

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

作用:通过已知的数据结构成员指针 ptr、数据结构 type、以及 ptr 在数据结构中的成员名,获取到指向数据结构 type 的指针。

首先可以看出 container_of 被预定义成一个函数(宏)。 函数的第一句话,通过 ((type*)0)->member 定义一个 MEMBER 型的指针 __mptr,这个指针指向 ptr,所以第一句话获取到了我们要求的结构体,它是成员 member 的地址(将 0 地址强制转换为数据结构 type 类型)。 函数的第一句话用这个地址减去成员 member 在结构体中的相对偏移量,就可以获取到所求结构体的地址,最后再把这个地址强制转换成 type 型指针,就获取到了所求结构体指针。define 预定义返回最后一句话的值,将所求结构体指针返回。

4.2 分析 offsetof

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

TYPE* 将整型常量 0 强制转换为 TYPE 型的指针,且这个指针指向的地址为 0,也就是将地址 0 开始的一块存储空间映射为 TYPE 型的对象,接下来再对结构体中 MEMBER 成员进行取址,而整个 TYPE 结构体的首地址是 0,这里获得的地址就是 MEMBER 成员在 TYPE 中的相对偏移量。再将这个偏移量强制转换成 size_t 型数据(无符号整型)。

所以整个 offsetof 的功能就是获取 MEMBER 成员在 TYPE 型数据中的偏移量。

4.3 分析 list_for_each_entry

#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_entry。list_for_each_entry 被预定义成一个 for 循环语句。

list_for_each_entry 被预定义成一个 for 循环语句,for 循环的第一句话获取 list_first_entry -> list_entry((ptr)->next, type, member) (ptr)->next 指向的 member 成员的数据结构指针,也就是将 pos 初始化为除链表头之外的第一个实体链表成员。

for 的第三句话通过 list_next_entry -> list_entry((pos)->member.next, typeof(*(pos)), member) (pos)->member.next 指针遍历整个实体链表。

当 pos->member.next 再次指向我们的链表头的时候跳出 for 循环。整个过程没有对链表头进行遍历(不需要被遍历),所以使用 list_for_each_entry 遍历链表必须从链表头开始。因此可以看出,list_for_each_entry 的功能就是遍历以 head 为链表头的实体链表,对实体链表中的数据结构进行处理。

目录

  1. 概述
  2. 用法
  3. 代码展示
  4. 代码分析
  5. 4.1 分析 container_of
  6. 4.2 分析 offsetof
  7. 4.3 分析 listforeach_entry

更多推荐文章

查看全部
  • 人工智能(AI)常见面试题及答案汇总
  • Linux 服务器部署 OpenClaw 教程
  • Java 串口通信库 jSerialComm 跨平台开发指南
  • Flutter 三方库 algolia_client_recommend 鸿蒙适配指南
  • 四款主流 AI 编程 IDE 横向评测:从辅助到代理的演进路径
  • 从销售助理转行软件测试:零经验求职与学习路径分享
  • SpringBoot Java 银行排队叫号系统
  • 新手如何从零开始学习漏洞挖掘
  • JavaScript Proxy 代理机制与核心方法详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online