[Linux] 内核链表实现详解 - 教程

news/2025/11/25 12:11:02/文章来源:https://www.cnblogs.com/yangykaifa/p/19267817

Linux内核链表实现详解

目录

1. 概述

Linux内核链表是内核中最基础也是最重要的数据结构之一,它采用了"侵入式"链表的设计思想,具有高效、灵活、通用的特点。本文档详细介绍Linux内核链表的实现原理、操作函数、应用实例以及设计思想。

2. 基本结构

Linux内核中链表的核心结构定义在include/linux/list.h文件中,其基本结构如下:

struct list_head {
struct list_head *next;  // 指向下一个节点的指针
struct list_head *prev;  // 指向前一个节点的指针
};

这是一个双向链表节点,只包含两个指针:一个指向下一个节点,一个指向前一个节点。

2.1 链表结构示意图

下图展示了Linux内核链表的基本结构和工作原理:

在这里插入图片描述

如图所示,Linux内核链表是一个双向循环链表,其中:

  • 链表头是一个struct list_head结构
  • 每个节点都包含一个嵌入的struct list_head成员
  • 链表节点通过nextprev指针相互连接
  • 链表头的next指向第一个节点,prev指向最后一个节点
  • 最后一个节点的next指向链表头,形成一个循环

链表的初始化方式有两种:

  1. 静态初始化
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
  1. 动态初始化
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}

初始化时,链表头的nextprev指针都指向自身,表示一个空链表。

3. 实现原理

3.1 侵入式链表设计

Linux内核链表采用了"侵入式"设计。在传统链表中,链表节点通常包含数据或指向数据的指针:

// 传统链表节点
struct traditional_node {
void *data;           // 指向数据的指针
struct traditional_node *next;
struct traditional_node *prev;
};

而在Linux内核的侵入式链表中,链表节点被嵌入到数据结构中:

// 数据结构
struct my_data {
int id;
char name[16];
struct list_head list;  // 嵌入的链表节点
// 其他数据...
};

3.2 container_of宏

container_of宏是侵入式链表设计的关键,它允许从链表节点指针获取包含该节点的结构体指针:

/**
* container_of - 从成员指针获取包含它的结构体指针
* @ptr:        指向成员的指针
* @type:       包含该成员的结构体类型
* @member:     成员在结构体中的名称
*
* 返回值: 指向包含该成员的结构体的指针
*/
#define container_of(ptr, type, member) ({                      \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
(type *)( (char *)__mptr - offsetof(type,member) );})

这个宏的工作原理是:

  1. 计算成员在结构体中的偏移量(使用offsetof宏)
  2. 从成员的地址减去这个偏移量,得到结构体的起始地址
  3. 将结果转换为适当的结构体指针类型

下面是一个图示,展示了container_of宏的工作原理:

内存布局:
+---------------------------+
| struct my_data           |
|                           |
| int id;                   |  <-- 结构体起始地址
| char name[16];            |
|                           |
| struct list_head list;    |  <-- 链表节点地址 (ptr)
|                           |
+---------------------------+
offsetof(struct my_data, list) = 链表节点地址 - 结构体起始地址
container_of(ptr, struct my_data, list) = 链表节点地址 - offsetof(struct my_data, list)= 结构体起始地址

基于container_of宏,Linux内核定义了list_entry宏,用于从链表节点获取包含它的结构体:

/**
* list_entry - 从链表节点获取包含它的结构体
* @ptr:    链表节点指针
* @type:   包含该节点的结构体类型
* @member: 链表节点在结构体中的名称
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

4. 主要操作函数

Linux内核链表提供了丰富的操作函数,使得链表操作变得简单高效。下面是这些函数的详细介绍。

4.1 链表操作函数参考表

函数名功能描述时间复杂度使用场景
INIT_LIST_HEAD初始化链表头或节点O(1)动态初始化链表
list_add在指定节点之后添加新节点O(1)实现栈(后进先出)
list_add_tail在指定节点之前添加新节点O(1)实现队列(先进先出)
list_del从链表中删除节点O(1)移除不再需要的节点
list_del_init删除节点并重新初始化O(1)移除节点后可能重新使用
list_replace用新节点替换旧节点O(1)替换链表中的节点
list_move将节点移动到另一个链表的头部O(1)节点在链表间移动
list_move_tail将节点移动到另一个链表的尾部O(1)节点在链表间移动
list_is_last检查节点是否是链表的最后一个节点O(1)判断节点位置
list_empty检查链表是否为空O(1)判断链表状态
list_is_singular检查链表是否只有一个节点O(1)判断链表状态
list_cut_position将链表分割为两个O(1)链表分割操作
list_splice将一个链表合并到另一个链表的头部O(1)合并两个链表
list_splice_tail将一个链表合并到另一个链表的尾部O(1)合并两个链表
list_splice_init合并后重新初始化源链表O(1)合并后清空源链表
list_splice_tail_init合并到尾部后重新初始化源链表O(1)合并后清空源链表

4.2 遍历宏参考表

宏名功能描述使用场景
list_for_each正向遍历链表节点只需访问链表节点
list_for_each_prev反向遍历链表节点从尾到头遍历
list_for_each_safe安全地正向遍历(可删除节点)遍历过程中可能删除节点
list_for_each_prev_safe安全地反向遍历(可删除节点)反向遍历过程中可能删除节点
list_for_each_entry遍历包含链表节点的结构体需要访问结构体数据
list_for_each_entry_reverse反向遍历包含链表节点的结构体从尾到头访问结构体
list_for_each_entry_safe安全地遍历结构体(可删除节点)遍历过程中可能删除节点
list_for_each_entry_safe_reverse安全地反向遍历结构体(可删除节点)反向遍历过程中可能删除节点
list_for_each_entry_continue从当前位置继续遍历结构体中断后继续遍历
list_for_each_entry_from从指定位置开始遍历结构体从特定位置开始遍历

4.3 添加节点

4.1.1 list_add - 在指定节点之后添加新节点
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
4.1.2 list_add_tail - 在指定节点之前添加新节点
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}

4.2 删除节点

4.2.1 list_del - 从链表中删除节点
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
4.2.2 list_del_init - 删除节点并重新初始化
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}

4.3 移动节点

4.3.1 list_move - 将节点从一个链表移动到另一个链表的头部
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
4.3.2 list_move_tail - 将节点从一个链表移动到另一个链表的尾部
static inline void list_move_tail(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}

4.4 链表合并

4.4.1 list_splice - 将一个链表合并到另一个链表的头部
static inline void list_splice(const struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
4.4.2 list_splice_tail - 将一个链表合并到另一个链表的尾部
static inline void list_splice_tail(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}

4.5 链表状态检查

4.5.1 list_empty - 检查链表是否为空
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
4.5.2 list_is_singular - 检查链表是否只有一个节点
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}

5. 链表遍历

Linux内核提供了多种遍历链表的宏,使得链表遍历变得简单高效:

5.1 基本遍历

#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

5.2 安全遍历(防止遍历过程中删除节点)

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)

5.3 遍历包含链表节点的结构体

#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))

5.4 安全地遍历包含链表节点的结构体

#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))

6. 应用实例

6.1 进程管理

Linux内核中的进程管理是链表应用的典型例子:

struct task_struct {
// ...
struct list_head tasks;        // 用于所有进程的链表
struct list_head children;     // 用于子进程链表
struct list_head sibling;      // 用于兄弟进程链表
// ...
};

6.2 内存管理

在伙伴系统(Buddy System)中,每个空闲页块链表都使用list_head

struct zone {
// ...
struct free_area free_area[MAX_ORDER];
// ...
};
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};

6.3 文件系统

在目录项缓存(dentry cache)中,每个目录项都包含一个链表节点:

struct dentry {
// ...
struct list_head d_lru;        // 用于LRU链表
struct list_head d_child;      // 用于子目录项链表
struct list_head d_subdirs;    // 用于子目录链表
// ...
};

6.4 完整示例

下面是一个完整的示例,展示如何在内核模块中使用链表:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/list.h>#include <linux/slab.h>// 定义数据结构struct my_device {int id;char name[16];struct list_head list;  // 嵌入的链表节点};// 定义全局链表头static LIST_HEAD(device_list);// 初始化函数static int __init my_init(void){int i;struct my_device *dev;printk(KERN_INFO "链表示例模块加载\n");// 创建5个设备并添加到链表for (i = 0; i < 5; i++) {// 分配内存dev = kmalloc(sizeof(struct my_device), GFP_KERNEL);if (!dev) {printk(KERN_ERR "内存分配失败\n");return -ENOMEM;}// 初始化设备dev->id = i;snprintf(dev->name, sizeof(dev->name), "设备%d", i);// 添加到链表list_add_tail(&dev->list, &device_list);printk(KERN_INFO "添加设备: %s (ID: %d)\n", dev->name, dev->id);}// 遍历并打印链表printk(KERN_INFO "链表中的设备:\n");list_for_each_entry(dev, &device_list, list) {printk(KERN_INFO "  %s (ID: %d)\n", dev->name, dev->id);}return 0;}// 退出函数static void __exit my_exit(void){struct my_device *dev, *tmp;// 安全地遍历并释放链表list_for_each_entry_safe(dev, tmp, &device_list, list) {printk(KERN_INFO "删除设备: %s (ID: %d)\n", dev->name, dev->id);list_del(&dev->list);kfree(dev);}printk(KERN_INFO "链表示例模块卸载\n");}module_init(my_init);module_exit(my_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("示例作者");MODULE_DESCRIPTION("Linux内核链表示例模块");

7. 设计思想

7.1 简单而强大的设计哲学

Linux内核链表的设计遵循了"简单而强大"的哲学。它的核心结构非常简单,只有两个指针,但通过巧妙的设计和宏的使用,提供了强大的功能。

7.2 通用性与特殊性的平衡

Linux内核链表设计了一个通用的框架,可以适用于各种场景,同时又提供了针对特定场景的优化(如哈希链表)。

7.3 侵入式设计的思想

侵入式设计是Linux内核链表的核心思想,它将数据结构和链表节点紧密结合,提高了内存和缓存效率。

7.4 宏的巧妙运用

Linux内核链表大量使用宏来简化代码,提高可读性和可维护性。例如,container_of宏和各种遍历宏使得链表操作变得简单直观。

7.5 性能优先的设计

Linux内核链表的设计始终将性能放在首位,例如:

  1. 所有基本操作都是O(1)复杂度
  2. 侵入式设计提高了缓存命中率
  3. 避免了不必要的内存分配和释放

8. 参考资料

  1. Linux内核源代码:include/linux/list.h
  2. 《Linux内核设计与实现》,作者:Robert Love
  3. 《深入理解Linux内核》,作者:Daniel P. Bovet, Marco Cesati

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/975837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考虑到这个人非常讨厌 stl 的 vector,所以这个人

不需使用vector细数 vector 的罪恶。弱智的占用内存。 弱智的释放内存。 弱智的常数极大。 弱智的地址乱飘。 弱智的内存扩容。template<class Tp> struct Vector {Tp *st, *ed;uint sz;inline uint size() { re…

2025年口碑好的实木板高评价厂家推荐榜

2025年口碑好的实木板高评价厂家推荐榜行业背景与市场趋势实木板材作为家居装修和家具制造的基础材料,近年来随着消费者环保意识的提升和品质需求的增长,市场规模持续扩大。根据中国林产工业协会最新数据,2024年中国…

2025年知名的AAA纸箱厂家最新TOP实力排行

2025年知名的AAA纸箱厂家最新TOP实力排行行业背景与市场趋势随着全球制造业的持续发展和电子商务的蓬勃兴起,工业包装行业迎来了前所未有的发展机遇。根据中国包装联合会最新发布的《2024-2025中国包装工业发展报告》…

[nanoGPT] 性能与效率 | `torch.compile()` |`Flash Attention`|`混合精度训练`|`estimate_mfu` - 指南

[nanoGPT] 性能与效率 | `torch.compile()` |`Flash Attention`|`混合精度训练`|`estimate_mfu` - 指南2025-11-25 12:04 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: norm…

2025年热门的耐醋酸涂料厂家最新权威推荐排行榜

2025年热门的耐醋酸涂料厂家最新权威推荐排行榜行业背景与市场趋势耐醋酸涂料作为工业防护涂层的重要分支,近年来随着化工、食品加工、制药等行业的快速发展,市场需求持续增长。据中国涂料工业协会最新数据显示,202…

2025 年 11 月建筑资质服务权威推荐榜:资质代办/办理,设计资质,资质转让/交易/收购,新办与劳务资质一站式高效解决方案

2025 年 11 月建筑资质服务权威推荐榜:资质代办/办理,设计资质,资质转让/交易/收购,新办与劳务资质一站式高效解决方案 行业背景与发展趋势 建筑行业作为国民经济的重要支柱产业,其规范化发展离不开完善的资质管理…

2025年质量好的可升降课桌椅热门厂家推荐榜单

2025年质量好的可升降课桌椅热门厂家推荐榜单行业背景与市场趋势近年来,随着教育装备行业的快速发展和教育理念的不断升级,可升降课桌椅市场迎来了爆发式增长。据中国教育装备行业协会最新数据显示,2024年我国可升降…

2025年靠谱的超薄型防火涂料热门厂家推荐榜单

2025年靠谱的超薄型防火涂料热门厂家推荐榜单 行业背景与市场趋势 随着建筑安全标准的不断提高,防火涂料作为被动防火体系的重要组成部分,市场需求持续增长。据《2024年中国防火涂料行业分析报告》显示,2023年国内…

上海元音琴院:专业古琴教学机构的文化传承之路

在繁华现代的上海都市中,有一处能让心灵沉静下来的文化净土——上海元音琴院。作为沪上知名的古音琴院,这家专业的古琴教学机构自2008年创立以来,始终致力于古琴艺术的传承与推广,成为众多古琴爱好者学习交流的首选…

2025年建房专用胶合建筑模板品牌厂家排行榜

2025年建房专用胶合建筑模板品牌厂家排行榜行业背景与市场趋势随着中国建筑业的持续发展和城镇化进程的加速推进,胶合建筑模板作为建筑施工中不可或缺的材料,市场规模呈现稳定增长态势。根据中国林业产业联合会最新数…

2025年评价高的同步缓冲托底轨实力厂家TOP推荐榜

2025年评价高的同步缓冲托底轨实力厂家TOP推荐榜行业背景与市场趋势随着中国家居建材行业的持续升级,五金配件作为家居系统的重要组成部分,正经历着从功能性向品质化、智能化方向的转型。根据中国五金制品协会2024年…

2025年11月劳保鞋工厂避坑指南:客观评价与可操作性建议

在工业安全防护领域,劳保鞋作为保障劳动者脚部安全的关键装备,其选择直接影响工作场景的安全性与舒适性。许多用户可能是企业采购负责人、安全管理员或个体劳动者,他们需要可靠、耐用且符合行业标准的劳保鞋供应商。…

详细介绍:hadoop之MapReduce的map工作流程

详细介绍:hadoop之MapReduce的map工作流程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

上海元音琴院:在七弦清音中探寻千年文脉的专业古琴教学机构

在快节奏的现代都市生活中,如何找到一方心灵栖息之地?上海元音琴院作为沪上知名的古琴教学机构,正以其深厚的文化底蕴和专业的教学体系,为众多古琴爱好者开启通往传统音乐艺术的大门。 专业古琴教学机构的卓越品质…

2025年热门的蛇形帘窗饰厂家最新用户好评榜

2025年热门的蛇形帘窗饰厂家最新用户好评榜行业背景与市场趋势随着现代建筑设计的不断演进和消费者对家居美学的日益重视,窗饰行业正经历着前所未有的技术革新与市场扩张。据《2024-2025全球窗饰市场报告》显示,全球…

2025年评价高的全品类五金厂家最新权威实力榜

2025年评价高的全品类五金厂家最新权威实力榜行业背景与市场趋势五金行业作为制造业和建筑业的重要支撑,近年来呈现出稳健增长态势。据中国五金制品协会最新数据显示,2024年中国五金行业市场规模已达1.8万亿元,同比…

2025年评价高的油雾空气过滤器厂家最新权威推荐排行榜

2025年评价高的油雾空气过滤器厂家最新权威推荐排行榜行业背景与市场趋势随着工业4.0时代的深入发展和环保法规的日益严格,油雾空气过滤器作为工业生产中不可或缺的环保设备,其市场需求持续增长。据《2024-2025中国工…

2025年11月劳保鞋工厂推荐榜单:一份基于权威数据的工厂选择指南

在制造业、重工业、石油化工及能源等高风险作业环境中,一双合格的劳保鞋是保障劳动者安全的重要防线。作为企业采购负责人或安全管理者,您在挑选劳保鞋供应商时,不仅关注产品的基本防护性能,更看重工厂的综合实力、…

关于海外仓尾程派送费用难计算的问题!如何解决?

做海外仓的老板都知道,海外仓的计费一般就分三大块,分别是仓储费、库内操作费、物流费,其中尾程运费是海外仓盈利的关键盈利模块:通过向上游物流商获取低价运费,再增加差价卖给商家,这部分差价构成物流费部分的利…

2025年11月劳保鞋工厂推荐:一份基于多维度数据与用户需求的专业榜单

在制造业、建筑业、石油化工等工业领域,劳保鞋是保障从业人员安全的基础装备。选择一家可靠的劳保鞋工厂,不仅关乎采购成本,更直接关系到工作人员的生命安全与作业效率。当前,劳保鞋市场产品种类繁多,质量参差不齐…