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 等宏。

目录
相关文章
|
2月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
|
2月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
129 15
|
3月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
3月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
3月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
3月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
3月前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
63 3
|
3月前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
4月前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
92 26
|
4月前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
115 1