Linux 系统内核列表宏深度解析
0. 引言
在 Linux 内核中,链表是使用最广泛的数据结构之一。从进程管理到设备驱动,从内存管理到文件系统,几乎每个子系统都离不开链表的身影。Linux 内核链表的设计堪称经典,它通过侵入式设计 和宏定义实现了类型无关的通用链表操作,这种设计思想值得每一位系统级程序员深入学习和掌握。
本文目标读者:
- 嵌入式 Linux 开发者
- 内核驱动工程师
- 对数据结构设计感兴趣的 C 语言程序员
- 希望深入理解 Linux 内核设计思想的学习者
本文学习目标:
- 理解 Linux 内核链表的设计原理
- 掌握核心宏的使用方法
- 了解与传统链表的差异和优势
- 学会避免常见的使用陷阱
1. Linux 链表是如何做到类型无关的?
1.1 核心数据结构:list_head
传统链表通常将数据和指针放在一起,而 Linux 内核采用了侵入式设计------将链表节点嵌入到数据结构体中:
c
struct list_head {
struct list_head *next, *prev; // 仅包含双向指针,无数据域
};
设计精髓 :list_head 不包含任何业务数据,它只是一个纯粹的链表节点。这种设计使得同一套链表操作可以应用于任意结构体。
1.2 与传统链表的对比
| 特性 | 传统链表 | Linux 内核链表 |
|---|---|---|
| 结构设计 | 数据 + 指针 | 仅指针,数据通过偏移计算获取 |
| 类型依赖 | 每种数据类型需单独实现链表 | 一套宏支持所有类型 |
| 内存开销 | 每个节点额外分配指针结构体 | 零额外开销 |
| 灵活性 | 一个结构体只能属于一个链表 | 可嵌入多个 list_head |
| 缓存友好 | 数据和指针分离存储 | 数据和指针连续存储 |
1.3 内存布局示意
传统链表布局:
┌─────────────┐ ┌─────────────┐
│ data1 │ │ data2 │
├─────────────┤ ├─────────────┤
│ next ───────┼───>│ next ───────┼───> NULL
│ prev ───────┼───>│ prev ───────┼───> ...
└─────────────┘ └─────────────┘
Linux链表布局:
┌─────────────────────────────┐
│ struct student │
│ int id; │
│ char name[20]; │
│ int age; │
│ struct list_head list; │<─── 嵌入的链表节点
│ list.next ────────────┼───> 下一个节点的 list
│ list.prev ────────────┼───> 上一个节点的 list
└─────────────────────────────┘
思考题 :如果 list_head 不是结构体的第一个成员,list_entry 宏是否依然有效?为什么?
2. 核心操作宏详解与使用场景
2.1 完整的 list.h 实现
c
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
// 核心链表节点结构
struct list_head {
struct list_head *next, *prev;
};
// 静态初始化:编译期将 next/prev 指向自身(空链表状态)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
// 声明并初始化链表头变量
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
// 动态初始化:运行时初始化链表节点
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
// 【核心】内部插入函数:在 prev 和 next 之间插入 new
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
// 头部插入:在 head 之后插入(适用于栈式操作)
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
// 尾部插入:在 head 之前插入(适用于队列式操作)
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
// 【核心】内部删除函数
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
// 删除节点:将节点从链表中移除并清空指针
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = (void *)0; // 标记为已删除
entry->prev = (void *)0;
}
// 判断链表是否为空
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
// 【核心中的核心】从链表节点指针获取容器结构体指针
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
// 获取链表第一个元素
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
// 获取链表最后一个元素
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
// 遍历链表节点(不支持删除)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
// 【安全遍历】遍历链表节点(支持删除操作)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
// 遍历并直接获取结构体指针
#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))
// 获取下一个结构体元素
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
#endif
2.2 插入操作图解(Mermaid)
插入前状态:
#mermaid-svg-PLRCyy37l5uVtmwE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PLRCyy37l5uVtmwE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PLRCyy37l5uVtmwE .error-icon{fill:#552222;}#mermaid-svg-PLRCyy37l5uVtmwE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PLRCyy37l5uVtmwE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PLRCyy37l5uVtmwE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PLRCyy37l5uVtmwE .marker.cross{stroke:#333333;}#mermaid-svg-PLRCyy37l5uVtmwE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PLRCyy37l5uVtmwE p{margin:0;}#mermaid-svg-PLRCyy37l5uVtmwE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PLRCyy37l5uVtmwE .cluster-label text{fill:#333;}#mermaid-svg-PLRCyy37l5uVtmwE .cluster-label span{color:#333;}#mermaid-svg-PLRCyy37l5uVtmwE .cluster-label span p{background-color:transparent;}#mermaid-svg-PLRCyy37l5uVtmwE .label text,#mermaid-svg-PLRCyy37l5uVtmwE span{fill:#333;color:#333;}#mermaid-svg-PLRCyy37l5uVtmwE .node rect,#mermaid-svg-PLRCyy37l5uVtmwE .node circle,#mermaid-svg-PLRCyy37l5uVtmwE .node ellipse,#mermaid-svg-PLRCyy37l5uVtmwE .node polygon,#mermaid-svg-PLRCyy37l5uVtmwE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PLRCyy37l5uVtmwE .rough-node .label text,#mermaid-svg-PLRCyy37l5uVtmwE .node .label text,#mermaid-svg-PLRCyy37l5uVtmwE .image-shape .label,#mermaid-svg-PLRCyy37l5uVtmwE .icon-shape .label{text-anchor:middle;}#mermaid-svg-PLRCyy37l5uVtmwE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PLRCyy37l5uVtmwE .rough-node .label,#mermaid-svg-PLRCyy37l5uVtmwE .node .label,#mermaid-svg-PLRCyy37l5uVtmwE .image-shape .label,#mermaid-svg-PLRCyy37l5uVtmwE .icon-shape .label{text-align:center;}#mermaid-svg-PLRCyy37l5uVtmwE .node.clickable{cursor:pointer;}#mermaid-svg-PLRCyy37l5uVtmwE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PLRCyy37l5uVtmwE .arrowheadPath{fill:#333333;}#mermaid-svg-PLRCyy37l5uVtmwE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PLRCyy37l5uVtmwE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PLRCyy37l5uVtmwE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PLRCyy37l5uVtmwE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PLRCyy37l5uVtmwE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PLRCyy37l5uVtmwE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PLRCyy37l5uVtmwE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PLRCyy37l5uVtmwE .cluster text{fill:#333;}#mermaid-svg-PLRCyy37l5uVtmwE .cluster span{color:#333;}#mermaid-svg-PLRCyy37l5uVtmwE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PLRCyy37l5uVtmwE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PLRCyy37l5uVtmwE rect.text{fill:none;stroke-width:0;}#mermaid-svg-PLRCyy37l5uVtmwE .icon-shape,#mermaid-svg-PLRCyy37l5uVtmwE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PLRCyy37l5uVtmwE .icon-shape p,#mermaid-svg-PLRCyy37l5uVtmwE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PLRCyy37l5uVtmwE .icon-shape .label rect,#mermaid-svg-PLRCyy37l5uVtmwE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PLRCyy37l5uVtmwE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PLRCyy37l5uVtmwE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PLRCyy37l5uVtmwE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} head
node1
插入后状态(list_add - 头部插入):
#mermaid-svg-DRXywbvYJC8KX341{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DRXywbvYJC8KX341 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DRXywbvYJC8KX341 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DRXywbvYJC8KX341 .error-icon{fill:#552222;}#mermaid-svg-DRXywbvYJC8KX341 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DRXywbvYJC8KX341 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DRXywbvYJC8KX341 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DRXywbvYJC8KX341 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DRXywbvYJC8KX341 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DRXywbvYJC8KX341 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DRXywbvYJC8KX341 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DRXywbvYJC8KX341 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DRXywbvYJC8KX341 .marker.cross{stroke:#333333;}#mermaid-svg-DRXywbvYJC8KX341 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DRXywbvYJC8KX341 p{margin:0;}#mermaid-svg-DRXywbvYJC8KX341 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DRXywbvYJC8KX341 .cluster-label text{fill:#333;}#mermaid-svg-DRXywbvYJC8KX341 .cluster-label span{color:#333;}#mermaid-svg-DRXywbvYJC8KX341 .cluster-label span p{background-color:transparent;}#mermaid-svg-DRXywbvYJC8KX341 .label text,#mermaid-svg-DRXywbvYJC8KX341 span{fill:#333;color:#333;}#mermaid-svg-DRXywbvYJC8KX341 .node rect,#mermaid-svg-DRXywbvYJC8KX341 .node circle,#mermaid-svg-DRXywbvYJC8KX341 .node ellipse,#mermaid-svg-DRXywbvYJC8KX341 .node polygon,#mermaid-svg-DRXywbvYJC8KX341 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DRXywbvYJC8KX341 .rough-node .label text,#mermaid-svg-DRXywbvYJC8KX341 .node .label text,#mermaid-svg-DRXywbvYJC8KX341 .image-shape .label,#mermaid-svg-DRXywbvYJC8KX341 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DRXywbvYJC8KX341 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DRXywbvYJC8KX341 .rough-node .label,#mermaid-svg-DRXywbvYJC8KX341 .node .label,#mermaid-svg-DRXywbvYJC8KX341 .image-shape .label,#mermaid-svg-DRXywbvYJC8KX341 .icon-shape .label{text-align:center;}#mermaid-svg-DRXywbvYJC8KX341 .node.clickable{cursor:pointer;}#mermaid-svg-DRXywbvYJC8KX341 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DRXywbvYJC8KX341 .arrowheadPath{fill:#333333;}#mermaid-svg-DRXywbvYJC8KX341 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DRXywbvYJC8KX341 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DRXywbvYJC8KX341 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DRXywbvYJC8KX341 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DRXywbvYJC8KX341 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DRXywbvYJC8KX341 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DRXywbvYJC8KX341 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DRXywbvYJC8KX341 .cluster text{fill:#333;}#mermaid-svg-DRXywbvYJC8KX341 .cluster span{color:#333;}#mermaid-svg-DRXywbvYJC8KX341 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DRXywbvYJC8KX341 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DRXywbvYJC8KX341 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DRXywbvYJC8KX341 .icon-shape,#mermaid-svg-DRXywbvYJC8KX341 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DRXywbvYJC8KX341 .icon-shape p,#mermaid-svg-DRXywbvYJC8KX341 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DRXywbvYJC8KX341 .icon-shape .label rect,#mermaid-svg-DRXywbvYJC8KX341 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DRXywbvYJC8KX341 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DRXywbvYJC8KX341 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DRXywbvYJC8KX341 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} head
new
node1
插入后状态(list_add_tail - 尾部插入):
#mermaid-svg-xvDSUhE3DTOlvE6V{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xvDSUhE3DTOlvE6V .error-icon{fill:#552222;}#mermaid-svg-xvDSUhE3DTOlvE6V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xvDSUhE3DTOlvE6V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xvDSUhE3DTOlvE6V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xvDSUhE3DTOlvE6V .marker.cross{stroke:#333333;}#mermaid-svg-xvDSUhE3DTOlvE6V svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xvDSUhE3DTOlvE6V p{margin:0;}#mermaid-svg-xvDSUhE3DTOlvE6V .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster-label text{fill:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster-label span{color:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster-label span p{background-color:transparent;}#mermaid-svg-xvDSUhE3DTOlvE6V .label text,#mermaid-svg-xvDSUhE3DTOlvE6V span{fill:#333;color:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V .node rect,#mermaid-svg-xvDSUhE3DTOlvE6V .node circle,#mermaid-svg-xvDSUhE3DTOlvE6V .node ellipse,#mermaid-svg-xvDSUhE3DTOlvE6V .node polygon,#mermaid-svg-xvDSUhE3DTOlvE6V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xvDSUhE3DTOlvE6V .rough-node .label text,#mermaid-svg-xvDSUhE3DTOlvE6V .node .label text,#mermaid-svg-xvDSUhE3DTOlvE6V .image-shape .label,#mermaid-svg-xvDSUhE3DTOlvE6V .icon-shape .label{text-anchor:middle;}#mermaid-svg-xvDSUhE3DTOlvE6V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xvDSUhE3DTOlvE6V .rough-node .label,#mermaid-svg-xvDSUhE3DTOlvE6V .node .label,#mermaid-svg-xvDSUhE3DTOlvE6V .image-shape .label,#mermaid-svg-xvDSUhE3DTOlvE6V .icon-shape .label{text-align:center;}#mermaid-svg-xvDSUhE3DTOlvE6V .node.clickable{cursor:pointer;}#mermaid-svg-xvDSUhE3DTOlvE6V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xvDSUhE3DTOlvE6V .arrowheadPath{fill:#333333;}#mermaid-svg-xvDSUhE3DTOlvE6V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xvDSUhE3DTOlvE6V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xvDSUhE3DTOlvE6V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xvDSUhE3DTOlvE6V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xvDSUhE3DTOlvE6V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xvDSUhE3DTOlvE6V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster text{fill:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V .cluster span{color:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xvDSUhE3DTOlvE6V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xvDSUhE3DTOlvE6V rect.text{fill:none;stroke-width:0;}#mermaid-svg-xvDSUhE3DTOlvE6V .icon-shape,#mermaid-svg-xvDSUhE3DTOlvE6V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xvDSUhE3DTOlvE6V .icon-shape p,#mermaid-svg-xvDSUhE3DTOlvE6V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xvDSUhE3DTOlvE6V .icon-shape .label rect,#mermaid-svg-xvDSUhE3DTOlvE6V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xvDSUhE3DTOlvE6V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xvDSUhE3DTOlvE6V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xvDSUhE3DTOlvE6V :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} head
node1
new
插入流程图:
#mermaid-svg-C9VSt12B3zxvQa8j{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-C9VSt12B3zxvQa8j .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C9VSt12B3zxvQa8j .error-icon{fill:#552222;}#mermaid-svg-C9VSt12B3zxvQa8j .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C9VSt12B3zxvQa8j .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C9VSt12B3zxvQa8j .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C9VSt12B3zxvQa8j .marker.cross{stroke:#333333;}#mermaid-svg-C9VSt12B3zxvQa8j svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C9VSt12B3zxvQa8j p{margin:0;}#mermaid-svg-C9VSt12B3zxvQa8j .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-C9VSt12B3zxvQa8j .cluster-label text{fill:#333;}#mermaid-svg-C9VSt12B3zxvQa8j .cluster-label span{color:#333;}#mermaid-svg-C9VSt12B3zxvQa8j .cluster-label span p{background-color:transparent;}#mermaid-svg-C9VSt12B3zxvQa8j .label text,#mermaid-svg-C9VSt12B3zxvQa8j span{fill:#333;color:#333;}#mermaid-svg-C9VSt12B3zxvQa8j .node rect,#mermaid-svg-C9VSt12B3zxvQa8j .node circle,#mermaid-svg-C9VSt12B3zxvQa8j .node ellipse,#mermaid-svg-C9VSt12B3zxvQa8j .node polygon,#mermaid-svg-C9VSt12B3zxvQa8j .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C9VSt12B3zxvQa8j .rough-node .label text,#mermaid-svg-C9VSt12B3zxvQa8j .node .label text,#mermaid-svg-C9VSt12B3zxvQa8j .image-shape .label,#mermaid-svg-C9VSt12B3zxvQa8j .icon-shape .label{text-anchor:middle;}#mermaid-svg-C9VSt12B3zxvQa8j .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C9VSt12B3zxvQa8j .rough-node .label,#mermaid-svg-C9VSt12B3zxvQa8j .node .label,#mermaid-svg-C9VSt12B3zxvQa8j .image-shape .label,#mermaid-svg-C9VSt12B3zxvQa8j .icon-shape .label{text-align:center;}#mermaid-svg-C9VSt12B3zxvQa8j .node.clickable{cursor:pointer;}#mermaid-svg-C9VSt12B3zxvQa8j .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C9VSt12B3zxvQa8j .arrowheadPath{fill:#333333;}#mermaid-svg-C9VSt12B3zxvQa8j .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C9VSt12B3zxvQa8j .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C9VSt12B3zxvQa8j .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C9VSt12B3zxvQa8j .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C9VSt12B3zxvQa8j .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C9VSt12B3zxvQa8j .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C9VSt12B3zxvQa8j .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C9VSt12B3zxvQa8j .cluster text{fill:#333;}#mermaid-svg-C9VSt12B3zxvQa8j .cluster span{color:#333;}#mermaid-svg-C9VSt12B3zxvQa8j div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-C9VSt12B3zxvQa8j .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C9VSt12B3zxvQa8j rect.text{fill:none;stroke-width:0;}#mermaid-svg-C9VSt12B3zxvQa8j .icon-shape,#mermaid-svg-C9VSt12B3zxvQa8j .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C9VSt12B3zxvQa8j .icon-shape p,#mermaid-svg-C9VSt12B3zxvQa8j .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C9VSt12B3zxvQa8j .icon-shape .label rect,#mermaid-svg-C9VSt12B3zxvQa8j .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C9VSt12B3zxvQa8j .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C9VSt12B3zxvQa8j .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C9VSt12B3zxvQa8j :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 开始
调用 list_add new head
获取 head->next
调用 __list_add new head head->next
设置 next->prev = new
设置 new->next = next
设置 new->prev = prev
设置 prev->next = new
结束
2.3 删除操作图解
#mermaid-svg-FX7usl6fHnb47dAc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FX7usl6fHnb47dAc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FX7usl6fHnb47dAc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FX7usl6fHnb47dAc .error-icon{fill:#552222;}#mermaid-svg-FX7usl6fHnb47dAc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FX7usl6fHnb47dAc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FX7usl6fHnb47dAc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FX7usl6fHnb47dAc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FX7usl6fHnb47dAc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FX7usl6fHnb47dAc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FX7usl6fHnb47dAc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FX7usl6fHnb47dAc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FX7usl6fHnb47dAc .marker.cross{stroke:#333333;}#mermaid-svg-FX7usl6fHnb47dAc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FX7usl6fHnb47dAc p{margin:0;}#mermaid-svg-FX7usl6fHnb47dAc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FX7usl6fHnb47dAc .cluster-label text{fill:#333;}#mermaid-svg-FX7usl6fHnb47dAc .cluster-label span{color:#333;}#mermaid-svg-FX7usl6fHnb47dAc .cluster-label span p{background-color:transparent;}#mermaid-svg-FX7usl6fHnb47dAc .label text,#mermaid-svg-FX7usl6fHnb47dAc span{fill:#333;color:#333;}#mermaid-svg-FX7usl6fHnb47dAc .node rect,#mermaid-svg-FX7usl6fHnb47dAc .node circle,#mermaid-svg-FX7usl6fHnb47dAc .node ellipse,#mermaid-svg-FX7usl6fHnb47dAc .node polygon,#mermaid-svg-FX7usl6fHnb47dAc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FX7usl6fHnb47dAc .rough-node .label text,#mermaid-svg-FX7usl6fHnb47dAc .node .label text,#mermaid-svg-FX7usl6fHnb47dAc .image-shape .label,#mermaid-svg-FX7usl6fHnb47dAc .icon-shape .label{text-anchor:middle;}#mermaid-svg-FX7usl6fHnb47dAc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FX7usl6fHnb47dAc .rough-node .label,#mermaid-svg-FX7usl6fHnb47dAc .node .label,#mermaid-svg-FX7usl6fHnb47dAc .image-shape .label,#mermaid-svg-FX7usl6fHnb47dAc .icon-shape .label{text-align:center;}#mermaid-svg-FX7usl6fHnb47dAc .node.clickable{cursor:pointer;}#mermaid-svg-FX7usl6fHnb47dAc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FX7usl6fHnb47dAc .arrowheadPath{fill:#333333;}#mermaid-svg-FX7usl6fHnb47dAc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FX7usl6fHnb47dAc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FX7usl6fHnb47dAc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FX7usl6fHnb47dAc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FX7usl6fHnb47dAc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FX7usl6fHnb47dAc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FX7usl6fHnb47dAc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FX7usl6fHnb47dAc .cluster text{fill:#333;}#mermaid-svg-FX7usl6fHnb47dAc .cluster span{color:#333;}#mermaid-svg-FX7usl6fHnb47dAc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FX7usl6fHnb47dAc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FX7usl6fHnb47dAc rect.text{fill:none;stroke-width:0;}#mermaid-svg-FX7usl6fHnb47dAc .icon-shape,#mermaid-svg-FX7usl6fHnb47dAc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FX7usl6fHnb47dAc .icon-shape p,#mermaid-svg-FX7usl6fHnb47dAc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FX7usl6fHnb47dAc .icon-shape .label rect,#mermaid-svg-FX7usl6fHnb47dAc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FX7usl6fHnb47dAc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FX7usl6fHnb47dAc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FX7usl6fHnb47dAc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 开始
调用 list_del entry
获取 entry->prev 和 entry->next
调用 __list_del prev next
设置 next->prev = prev
设置 prev->next = next
设置 entry->next = NULL
设置 entry->prev = NULL
结束
2.4 list_entry 宏的内存计算原理
这是整个链表实现中最精妙的部分:
c
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
计算过程图解:
内存地址: 0x00000000 (虚拟地址 0)
假设 struct student 布局:
┌────────────────────────────────┐
│ 0x00000000: int id; │ offset = 0
├────────────────────────────────┤
│ 0x00000004: char name[20]; │ offset = 4
├────────────────────────────────┤
│ 0x00000018: int age; │ offset = 24
├────────────────────────────────┤
│ 0x0000001C: struct list_head; │ offset = 28
│ next; │
│ prev; │
└────────────────────────────────┘
&((struct student *)0)->list = 0x1C (十进制 28)
当 ptr = &student->list = 0x1000001C 时:
结构体起始地址 = 0x1000001C - 28 = 0x10000000
原理说明:
(type *)0:将地址 0 强制转换为 type 类型指针&((type *)0)->member:计算 member 字段相对于结构体起始地址的偏移量(char *)(ptr) - offset:用链表节点地址减去偏移量,得到结构体起始地址
3. 实战示例:从学生管理到多链表场景
3.1 基础示例:学生信息管理
c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
// 定义包含链表节点的结构体
struct student {
int id;
char name[20];
int age;
struct list_head list; // 嵌入链表节点
};
void print_student(struct student *s) {
printf("ID: %d, Name: %s, Age: %d\n", s->id, s->name, s->age);
}
int main(void) {
// 创建链表头
LIST_HEAD(student_list);
// 创建学生节点
struct student *s1 = malloc(sizeof(struct student));
s1->id = 1;
snprintf(s1->name, sizeof(s1->name), "ZhangSan");
s1->age = 20;
INIT_LIST_HEAD(&s1->list); // 必须初始化!
struct student *s2 = malloc(sizeof(struct student));
s2->id = 2;
snprintf(s2->name, sizeof(s2->name), "LiSi");
s2->age = 21;
INIT_LIST_HEAD(&s2->list);
struct student *s3 = malloc(sizeof(struct student));
s3->id = 3;
snprintf(s3->name, sizeof(s3->name), "WangWu");
s3->age = 22;
INIT_LIST_HEAD(&s3->list);
// 插入节点
list_add(&s1->list, &student_list); // 插入头部
list_add(&s2->list, &student_list); // 插入头部(现在在 s1 前面)
list_add_tail(&s3->list, &student_list); // 插入尾部
// 遍历链表
printf("=== 遍历链表 ===\n");
struct student *stu;
list_for_each_entry(stu, &student_list, list) {
print_student(stu);
}
// 删除节点
printf("\n=== 删除 ID=2 的学生 ===\n");
list_for_each_entry(stu, &student_list, list) {
if (stu->id == 2) {
list_del(&stu->list);
free(stu);
break;
}
}
// 再次遍历
printf("\n=== 删除后的链表 ===\n");
list_for_each_entry(stu, &student_list, list) {
print_student(stu);
}
// 清理资源
list_del(&s1->list);
free(s1);
list_del(&s3->list);
free(s3);
return 0;
}
3.2 进阶示例:多链表管理(进程控制块场景)
在实际内核场景中,一个结构体可能需要同时属于多个链表。例如进程控制块可能需要按优先级、状态等多个维度组织:
c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
// 模拟进程控制块结构
struct task_struct {
int pid; // 进程ID
char comm[16]; // 进程名
int priority; // 优先级
int state; // 状态: 0=运行, 1=睡眠, 2=僵尸
// 多链表节点
struct list_head task_list; // 所有进程链表
struct list_head run_list; // 运行队列
struct list_head wait_list; // 等待队列
};
// 全局链表头
LIST_HEAD(all_tasks);
LIST_HEAD(running_tasks);
LIST_HEAD(waiting_tasks);
void print_task(struct task_struct *task) {
const char *state_str[] = {"RUNNING", "SLEEPING", "ZOMBIE"};
printf("PID: %d, Name: %s, Priority: %d, State: %s\n",
task->pid, task->comm, task->priority, state_str[task->state]);
}
int main(void) {
// 创建进程
struct task_struct *init = malloc(sizeof(struct task_struct));
init->pid = 1;
snprintf(init->comm, sizeof(init->comm), "init");
init->priority = 100;
init->state = 0; // RUNNING
INIT_LIST_HEAD(&init->task_list);
INIT_LIST_HEAD(&init->run_list);
INIT_LIST_HEAD(&init->wait_list);
struct task_struct *bash = malloc(sizeof(struct task_struct));
bash->pid = 2;
snprintf(bash->comm, sizeof(bash->comm), "bash");
bash->priority = 50;
bash->state = 0; // RUNNING
INIT_LIST_HEAD(&bash->task_list);
INIT_LIST_HEAD(&bash->run_list);
INIT_LIST_HEAD(&bash->wait_list);
struct task_struct *sleeping = malloc(sizeof(struct task_struct));
sleeping->pid = 3;
snprintf(sleeping->comm, sizeof(sleeping->comm), "sleep");
sleeping->priority = 30;
sleeping->state = 1; // SLEEPING
INIT_LIST_HEAD(&sleeping->task_list);
INIT_LIST_HEAD(&sleeping->run_list);
INIT_LIST_HEAD(&sleeping->wait_list);
// 加入多个链表
list_add_tail(&init->task_list, &all_tasks);
list_add_tail(&bash->task_list, &all_tasks);
list_add_tail(&sleeping->task_list, &all_tasks);
list_add_tail(&init->run_list, &running_tasks);
list_add_tail(&bash->run_list, &running_tasks);
list_add_tail(&sleeping->wait_list, &waiting_tasks);
// 遍历所有进程
printf("=== 所有进程 ===\n");
struct task_struct *task;
list_for_each_entry(task, &all_tasks, task_list) {
print_task(task);
}
// 遍历运行队列
printf("\n=== 运行队列 ===\n");
list_for_each_entry(task, &running_tasks, run_list) {
print_task(task);
}
// 遍历等待队列
printf("\n=== 等待队列 ===\n");
list_for_each_entry(task, &waiting_tasks, wait_list) {
print_task(task);
}
return 0;
}
4. 性能分析:为何 Linux 链表如此高效?
4.1 时间复杂度分析
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 插入(头部/尾部) | O(1) | 仅修改4个指针 |
| 删除 | O(1) | 仅修改2个指针 |
| 查找 | O(n) | 需要遍历 |
| 判断空链表 | O(1) | 仅比较指针 |
4.2 缓存友好性分析
侵入式设计的优势:
传统链表(缓存不友好):
CPU Cache Line: ┌────────────────────────────┐
│ Node* 指针 │ ← 缓存 miss
└────────────────────────────┘
┌────────────────────────────┐
│ 实际数据 │ ← 再次缓存 miss
└────────────────────────────┘
Linux链表(缓存友好):
CPU Cache Line: ┌────────────────────────────┐
│ struct student │
│ id, name, age, list │ ← 一次缓存加载
└────────────────────────────┘
空间局部性:数据和链表指针连续存储在同一缓存行中,减少缓存 miss。
时间局部性:遍历链表时,相邻节点通常在内存中连续分配,预取机制可提前加载。
5. 常见错误与陷阱
5.1 忘记初始化链表节点
错误代码:
c
struct student *s = malloc(sizeof(struct student));
s->id = 1;
list_add(&s->list, &student_list); // 未初始化!s->list.next/prev 是随机值
正确代码:
c
struct student *s = malloc(sizeof(struct student));
s->id = 1;
INIT_LIST_HEAD(&s->list); // 必须先初始化!
list_add(&s->list, &student_list);
5.2 在遍历中直接删除节点
错误代码:
c
list_for_each(pos, &student_list) {
struct student *stu = list_entry(pos, struct student, list);
if (stu->id == 2) {
list_del(pos); // 删除后 pos 变成悬空指针!
free(stu);
}
}
正确代码:使用安全遍历宏
c
struct list_head *pos, *n;
list_for_each_safe(pos, n, &student_list) {
struct student *stu = list_entry(pos, struct student, list);
if (stu->id == 2) {
list_del(pos);
free(stu);
}
}
5.3 重复删除节点
错误代码:
c
list_del(&s->list);
// ... 其他代码 ...
list_del(&s->list); // s->list.next/prev 已被设为 NULL,会崩溃!
建议:删除后立即将指针置 NULL 或使用标志位标记。
6. 单元测试建议
为链表操作编写单元测试是工程化开发的良好实践:
c
#include <assert.h>
void test_list_operations() {
LIST_HEAD(test_list);
// 测试空链表
assert(list_empty(&test_list) == 1);
// 创建测试节点
struct student *s1 = malloc(sizeof(struct student));
s1->id = 1;
INIT_LIST_HEAD(&s1->list);
// 测试插入
list_add(&s1->list, &test_list);
assert(list_empty(&test_list) == 0);
// 测试遍历
struct student *stu;
list_for_each_entry(stu, &test_list, list) {
assert(stu->id == 1);
}
// 测试删除
list_del(&s1->list);
free(s1);
assert(list_empty(&test_list) == 1);
printf("All tests passed!\n");
}
7. 设计优势对比
| 特性 | Linux 内核链表 | 传统链表 | 优势说明 |
|---|---|---|---|
| 类型通用性 | 一套宏支持所有类型 | 每种类型需单独实现 | 代码复用性高 |
| 内存开销 | 零额外开销 | 每个节点额外分配 | 节省内存 |
| 多链表支持 | 一个结构体可嵌入多个节点 | 难以实现 | 灵活性强 |
| 缓存友好 | 数据和指针连续存储 | 数据和指针分离 | 性能更好 |
| 代码简洁 | 宏展开为内联代码 | 函数调用开销 | 执行效率高 |
| 可维护性 | 集中维护一套宏 | 分散维护多套实现 | 易于维护 |
8. 总结与展望
8.1 核心知识点回顾
- 侵入式设计 :将
list_head嵌入到用户结构体中,实现类型无关 - list_entry 宏:通过偏移量计算从链表节点获取结构体指针
- O(1) 复杂度:插入和删除操作只需修改指针
- 缓存友好:数据和指针连续存储,提高缓存命中率
8.2 进阶思考
- 如何实现反向遍历宏
list_for_each_entry_reverse? - 如果需要在遍历过程中同时修改链表结构,应该注意什么?
- 如何实现链表的合并、拆分操作?
8.3 扩展阅读
- 内核源码 :
include/linux/list.h- Linux 内核链表实现 - 经典书籍:《Linux 内核设计与实现》第三章 - 进程管理中的链表应用
- 进阶数据结构 :Linux 内核中的哈希表 (
hlist)、红黑树 (rbtree)
附录:宏/函数速查表(按功能分类)
初始化
| 宏/函数 | 语法 |
|---|---|
LIST_HEAD(name) |
声明并初始化链表头 |
INIT_LIST_HEAD(list) |
动态初始化链表节点 |
插入
| 宏/函数 | 语法 |
|---|---|
list_add(new, head) |
头部插入 |
list_add_tail(new, head) |
尾部插入 |
删除
| 宏/函数 | 语法 |
|---|---|
list_del(entry) |
删除节点 |
遍历
| 宏/函数 | 语法 |
|---|---|
list_for_each(pos, head) |
遍历节点指针 |
list_for_each_safe(pos, n, head) |
安全遍历 |
list_for_each_entry(pos, head, member) |
遍历结构体 |
条目访问
| 宏/函数 | 语法 |
|---|---|
list_entry(ptr, type, member) |
获取结构体指针 |
list_first_entry(ptr, type, member) |
获取第一个元素 |
list_last_entry(ptr, type, member) |
获取最后一个元素 |
list_next_entry(pos, member) |
获取下一个元素 |