Linux内核中的list.h浅谈

简介:

[十月往昔]——Linux内核中的list.h浅谈

为什么要叫做“十月往昔”呢,是为了纪念我的原博客。

不知道为什么,突然想来一个新的开始——而那个博客存活至今刚好十个月,也有十个月里的文档。

十月往昔,总有一些觉得珍贵的,所以搬迁到这里来。

而这篇文章是在09.04.10里写的。

Jason Lee

 

————————————–cut-line

/*-------------------------------

include/linux/list.h -2.6.29

*/

该文件包含:

1#ifndef _LINUX_LIST_H 2#define _LINUX_LIST_H 3 4#include <linux/stddef.h> 5#include <linux/poison.h> 6#include <linux/prefetch.h> 7#include <asm/system.h>

 

 

链表的初始化

19struct list_head { 20 struct list_head *next, *prev; 21}; 22 23#define LIST_HEAD_INIT(name) { &(name), &(name) } 24 25#define LIST_HEAD(name) / 26 struct list_head name = LIST_HEAD_INIT(name) 27 28static inline void INIT_LIST_HEAD(struct list_head *list) 29{ 30 list->next = list; 31 list->prev = list; 32}

19-21 行定义了一个list_head 结构,只有两个指向list_head 结构的指针,一个next ,一个prev ,作用显而易见。

23 行的宏LIST_HEAD_INIT(name)25 行的宏LIST_HEAD(name) 组合进行链表的初始化,即nextprev 都指向自身。

25 行的静态内联函数INIT_LIST_HEAD(struct list_head *list) 同样是用来初始化链表,效果同上述一点。GNU 下的C 语言对C 进行了扩充,不再是ANSI C ,它里面增添了很多C++ 的特性,所以对内核进行编译只能选用相应的GCC

INIT_LIST_HEAD 在有的文献中是以宏的形式出现:

#define INIT_LIST_HEAD(ptr) do { / (ptr)->next = (ptr); (ptr)->prev = (ptr); / } while (0)

 

 

链表的插入

34/* 35 * Insert a new entry between two known consecutive entries. 36 * 37 * This is only for internal list manipulation where we know 38 * the prev/next entries already! 39 */ 40#ifndef CONFIG_DEBUG_LIST 41static inline void __list_add(struct list_head *new, 42 struct list_head *prev, 43 struct list_head *next) 44{ 45 next->prev = new; 46 new->next = next; 47 new->prev = prev; 48 prev->next = new; 49} 50#else 51extern void __list_add(struct list_head *new, 52 struct list_head *prev, 53 struct list_head *next); 54#endif

这段程序在两个已知的节点中间插入一个新节点。这里选择的是条件编译,如果没有对CONFIG_DEBUG_LIST 进行宏定义,则定义了__list_add 这个静态内联函数,便于以下两个函数使用。

56/** 57 * list_add - add a new entry 58 * @new: new entry to be added 59 * @head: list head to add it after 60 * 61 * Insert a new entry after the specified head. 62 * This is good for implementing stacks. 63 */ 64static inline void list_add(struct list_head *new, struct list_head *head) 65{ 66 __list_add(new, head, head->next); 67}

该函数在指定的head 节点后面插入一个新节点new

70/** 71 * list_add_tail - add a new entry 72 * @new: new entry to be added 73 * @head: list head to add it before 74 * 75 * Insert a new entry before the specified head. 76 * This is useful for implementing queues. 77 */ 78static inline void list_add_tail(struct list_head *new, struct list_head *head) 79{ 80 __list_add(new, head->prev, head); 81}

该函数将一个节点new 插在指定的节点head 之前。

 

链表的删除

83/* 84 * Delete a list entry by making the prev/next entries 85 * point to each other. 86 * 87 * This is only for internal list manipulation where we know 88 * the prev/next entries already! 89 */ 90static inline void __list_del(struct list_head * prev, struct list_head * next) 91{ 92 next->prev = prev; 93 prev->next = next; 94}

该函数通过设置prevnext 指针指向彼此,实现了删除二者之间节点的功能。但是这里我有个疑惑,删除的指针的释放在哪里实现。

96/** 97 * list_del - deletes entry from list. 98 * @entry: the element to delete from the list. 99 * Note: list_empty() on entry does not return true after this, the entry is 100 * in an undefined state. 101 */ 102#ifndef CONFIG_DEBUG_LIST 103static inline void list_del(struct list_head *entry) 104{ 105 __list_del(entry->prev, entry->next); 106 entry->next = LIST_POISON1; 107 entry->prev = LIST_POISON2; 108} 109#else 110extern void list_del(struct list_head *entry); 111#endif

该函数通过调用上面的内联函数实现节点的删除,这里的LIST_POISON1 LIST_POISON2 是在linux / poison . h 定义的。此处仍然是条件编译。

 

 

链表节点的置换

113/** 114 * list_replace - replace old entry by new one 115 * @old : the element to be replaced 116 * @new : the new element to insert 117 * 118 * If @old was empty, it will be overwritten. 119 */ 120static inline void list_replace(struct list_head *old, 121 struct list_head *new) 122{ 123 new->next = old->next; 124 new->next->prev = new; 125 new->prev = old->prev; 126 new->prev->next = new; 127} 128 129static inline void list_replace_init(struct list_head *old, 130 struct list_head *new) 131{ 132 list_replace(old, new); 133 INIT_LIST_HEAD(old); 134}

静态内联函数list_replace 接受两个参数:oldnew ,并通过new 替换old 。而list_replace_init 函数则是通过调用list_replace 进行替换,之后调用INIT_LIST_HEAD 对被替换的old 进行链表初始化。

 

 

链表的移动

146/** 147 * list_move - delete from one list and add as another’s head 148 * @list: the entry to move 149 * @head: the head that will precede our entry 150 */ 151static inline void list_move(struct list_head *list, struct list_head *head) 152{ 153 __list_del(list->prev, list->next); 154 list_add(list, head); 155}

List_move 函数接受两个参数,第一个参数list 为想要移动的节点指针,第二个参数为目的地节点指针。该函数通过调用__list_del 函数实现list 节点的prevnext 两个指针互指实现删除list 节点的效果,并且调用list_addlist 节点插入到head 之后。

157/** 158 * list_move_tail - delete from one list and add as another’s tail 159 * @list: the entry to move 160 * @head: the head that will follow our entry 161 */ 162static inline void list_move_tail(struct list_head *list, 163 struct list_head *head) 164{ 165 __list_del(list->prev, list->next); 166 list_add_tail(list, head); 167}

List_move_tail 函数将指定节点移到指定链表的尾部,成为尾节点。并且由于链表是循环的,所以移动的节点指向该链表head 节点。具体实现是通过目标节点的prevnext 互指实现从原始链表中删除list 节点,之后通过调用list_add_taillist 节点插入到以head 为表首的链表尾部。

 

判断节点是否为链表的最后一个

169/** 170 * list_is_last - tests whether @list is the last entry in list @head 171 * @list: the entry to test 172 * @head: the head of the list 173 */ 174static inline int list_is_last(const struct list_head *list, 175 const struct list_head *head) 176{ 177 return list->next == head; 178}

通过判断节点的next 指向是否为表首来确定是否为last

 

 

判断链表是否为空

180/** 181 * list_empty - tests whether a list is empty 182 * @head: the list to test. 183 */ 184static inline int list_empty(const struct list_head *head) 185{ 186 return head->next == head; 187}

通过判断head 节点是否指向自身来判断链表是否为空。

189/** 190 * list_empty_careful - tests whether a list is empty and not being modified 191 * @head: the list to test 192 * 193 * Description: 194 * tests whether a list is empty _and_ checks that no other CPU might be 195 * in the process of modifying either member (next or prev) 196 * 197 * NOTE: using list_empty_careful() without synchronization 198 * can only be safe if the only activity that can happen 199 * to the list entry is list_del_init(). Eg. it cannot be used 200 * if another CPU could re-list_add() it. 201 */ 202static inline int list_empty_careful(const struct list_head *head) 203{ 204 struct list_head *next = head->next; 205 return (next == head) && (next == head->prev); 206}

此处函数的作用并不十分理解,对于绿色注释说明部分的DescriptionNOTE 部分也是一知半解。单纯地翻一下NOTE 部分:如果没有经过同步化处理,那么如果要达到安全地使用list_empty_careful 这个函数必须限定当前能对指定节点发生的操作仅仅为list_del_init() ,比如当一个CPU 对它进行add 操作的时候不能使用该函数。

该函数能达到的效果是检查链表是否为空,并且检测是否有CPU 在修改当前指定节点的prevnext 指针。

这里引用一段解释,来自杨沙洲:

Linux 链表另行提供了一个 list_empty_careful() 宏,它同时判断头指针的 next prev ,仅当两者都指向自己时才返回真。这主要是为了应付另一个 cpu 正在处理同一个链表而造成 next prev 不一致的情况。但代码注释也承认,这一安全保障能力有限:除非其他 cpu 的链表操作只有 list_del_init() ,否则仍然不能保证安全,也就是说,还是需要加锁保护。

 

 

判断链表是否只有唯一的一个节点

208/** 209 * list_is_singular - tests whether a list has just one entry. 210 * @head: the list to test. 211 */ 212static inline int list_is_singular(const struct list_head *head) 213{ 214 return !list_empty(head) && (head->next == head->prev); 215}

空表并不是一个节点都没有,唯一的节点也不是指只有一个节点,具体看函数代码我们便可以了解。当一个节点指针被执行LIST_HEAD 了以后,它的prevnext 指针都指向自身,这便称为空表;而如果它的prevnext 指针都指向仅有的第二个节点,那么它便称为仅有一个节点。

 

 

链表的切割

217static inline void __list_cut_position(struct list_head *list, 218 struct list_head *head, struct list_head *entry) 219{ 220 struct list_head *new_first = entry->next; 221 list->next = head->next; 222 list->next->prev = list; 223 list->prev = entry; 224 entry->next = list; 225 head->next = new_first; 226 new_first->prev = head; 227} 228 229/** 230 * list_cut_position - cut a list into two 231 * @list: a new list to add all removed entries 232 * @head: a list with entries 233 * @entry: an entry within head, could be the head itself 234 * and if so we won’t cut the list 235 * 236 * This helper moves the initial part of @head, up to and 237 * including @entry, from @head to @list. You should 238 * pass on @entry an element you know is on @head. @list 239 * should be an empty list or a list you do not care about 240 * losing its data. 241 * 242 */ 243static inline void list_cut_position(struct list_head *list, 244 struct list_head *head, struct list_head *entry) 245{ 246 if (list_empty(head)) 247 return; 248 if (list_is_singular(head) && 249 (head->next != entry && head != entry)) 250 return; 251 if (entry == head) 252 INIT_LIST_HEAD(list); 253 else 254 __list_cut_position(list, head, entry); 255}

这里有三个参数,list,head,entry

假设原先有链表:head <-> node1 <-> node2 <-> node3 <-> entry <-> node4 <-> head

那么最后会得到链表1head <-> node4 <-> head 和链表2list <-> node1 <-> node2 <-> node3 <-> entry <-> list

这里最好自己画图模拟一下。

 

链表的合并

257static inline void __list_splice(const struct list_head *list, 258 struct list_head *prev, 259 struct list_head *next) 260{ 261 struct list_head *first = list->next; 262 struct list_head *last = list->prev; 263 264 first->prev = prev; 265 prev->next = first; 266 267 last->next = next; 268 next->prev = last; 269}

假设有两条链表:head <-> node1 <-> node2 <-> node3 <-> head

和:last <-> list <-> first

那么合并的结果是取代了headlast <-> list <-> first <-> node1 <-> node2 <-> node3 <-> last

271/** 272 * list_splice - join two lists, this is designed for stacks 273 * @list: the new list to add. 274 * @head: the place to add it in the first list. 275 */ 276static inline void list_splice(const struct list_head *list, 277 struct list_head *head) 278{ 279 if (!list_empty(list)) 280 __list_splice(list, head, head->next); 281} 282 283/** 284 * list_splice_tail - join two lists, each list being a queue 285 * @list: the new list to add. 286 * @head: the place to add it in the first list. 287 */ 288static inline void list_splice_tail(struct list_head *list, 289 struct list_head *head) 290{ 291 if (!list_empty(list)) 292 __list_splice(list, head->prev, head); 293} 294 295/** 296 * list_splice_init - join two lists and reinitialise the emptied list. 297 * @list: the new list to add. 298 * @head: the place to add it in the first list. 299 * 300 * The list at @list is reinitialised 301 */ 302static inline void list_splice_init(struct list_head *list, 303 struct list_head *head) 304{ 305 if (!list_empty(list)) { 306 __list_splice(list, head, head->next); 307 INIT_LIST_HEAD(list); 308 } 309} 310 311/** 312 * list_splice_tail_init - join two lists and reinitialise the emptied list 313 * @list: the new list to add. 314 * @head: the place to add it in the first list. 315 * 316 * Each of the lists is a queue. 317 * The list at @list is reinitialised 318 */ 319static inline void list_splice_tail_init(struct list_head *list, 320 struct list_head *head) 321{ 322 if (!list_empty(list)) { 323 __list_splice(list, head->prev, head); 324 INIT_LIST_HEAD(list); 325 } 326}

以下的合并函数都是调用第一个合并内联函数__list_splice ,区别只在于合并取代的位置以及是否对空出来的head 进行初始化,即调用INIT_LIST_HEAD 等宏。

目录
相关文章
|
1天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
14 6
|
1天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###
|
4天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
2天前
|
缓存 网络协议 Linux
Linux操作系统内核
Linux操作系统内核 1、进程管理: 进程调度 进程创建与销毁 进程间通信 2、内存管理: 内存分配与回收 虚拟内存管理 缓存管理 3、驱动管理: 设备驱动程序接口 硬件抽象层 中断处理 4、文件和网络管理: 文件系统管理 网络协议栈 网络安全及防火墙管理
19 4
|
4天前
|
人工智能 算法 大数据
Linux内核中的调度算法演变:从O(1)到CFS的优化之旅###
本文深入探讨了Linux操作系统内核中进程调度算法的发展历程,聚焦于O(1)调度器向完全公平调度器(CFS)的转变。不同于传统摘要对研究背景、方法、结果和结论的概述,本文创新性地采用“技术演进时间线”的形式,简明扼要地勾勒出这一转变背后的关键技术里程碑,旨在为读者提供一个清晰的历史脉络,引领其深入了解Linux调度机制的革新之路。 ###
|
6天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
30 4
|
7天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
9天前
|
缓存 Linux
揭秘Linux内核:探索CPU拓扑结构
【10月更文挑战第26天】
26 1
|
9天前
|
缓存 运维 Linux
深入探索Linux内核:CPU拓扑结构探测
【10月更文挑战第18天】在现代计算机系统中,CPU的拓扑结构对性能优化和资源管理至关重要。了解CPU的核心、线程、NUMA节点等信息,可以帮助开发者和系统管理员更好地调优应用程序和系统配置。本文将深入探讨如何在Linux内核中探测CPU拓扑结构,介绍相关工具和方法。
11 0
|
7天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
14 0
下一篇
无影云桌面