浅谈linux内核中的list.h[2]

Linux中List.h文件的分析和应用 本文介绍Linux中List.h文件,此文件中用双向链表实现了多种数据结构,比如队列,堆栈等,短小精湛,值得借鉴。此文件中还有哈希表的应用。 阅读详情

 

链表的删除

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}

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

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接受两个参数:old和new,并通过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节点的prev和next两个指针互指实现删除list节点的效果,并且调用list_add将list节点插入到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节点。具体实现是通过目标节点的prev和next互指实现从原始链表中删除list节点,之后通过调用list_add_tail将list节点插入到以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}

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

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

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

“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了以后,它的prev和next指针都指向自身,这便称为空表;而如果它的prev和next指针都指向仅有的第二个节点,那么它便称为仅有一个节点。

 

 

链表的切割

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

那么最后会得到链表1:head <-> node4 <-> head 和链表2:list <-> 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

那么合并的结果是取代了head:last <-> 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等宏。

 

list 移植与基本使用 阅读详情

相关推荐

Linux内核源码list.h解读

#list.h源码阅读 此文章是我阅读list.h后的一些见解,有问题且理解不到位的地方希望大家批评指正。 本次我们阅读的内核版本为4.18.7 在Linux内核中,list.h是内核为了方便使用链表而自己建立的链表头文件。 以__开头的是指内核函数。 ##链表 ##宏定义 除了调用其它头文件外,首先看到的是两个宏定义。 #define LIST_HEAD_INIT(name) { &amp;amp;...

weixin_43122409的博客 3934

【数据结构】list.h 常用函数实现详解

本文介绍了 list.h 的函数实现,对如何使用 list.h 有一定帮助。

wkd_007的博客 2365

linux内核中的list.h文件中哈希表的分析

一、背景     本篇文章将先从对哈希表的基本介绍开始,然后分析linux内核(3.10.0版)中的list.h文件中的哈希表。 二、简述哈希表     根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为哈希表。     上面这一段是从一本教材中摘抄出来的对哈希表的一段描述。    

hty46565的博客 2960

【数据结构】list.h 详细使用教程 -- 附带例子代码

介绍list.h 的详细用法,并给出例子代码

wkd_007的博客 1994

Linux内核经典队列queue.h详解

一、前言 相信有一定编程经验的你,在项目中经常会遇到队列、链表、ringbuffer等问题。刚开始的时候,都有过自己编写代码(造轮子)的经验。但随着工作经验的提升,经常会发现: 以前自己造的轮子还有很多漏洞,并不能满足现在或将来的产品需求;甚至已经给现有产品埋下了坑; 接触的越来越多,发现有很多经典开源的库(GitHub),已经实现了以前自己造的轮子,而且开源库做的更好(应该是非常的好)。 那...

小银子 5843

【Linux高级编译】Linux内核的list.h的高效应用----单向链表和双向链表的实现

Linux内核中,有个非常经典的链表实现,你了解过它的实现吗?

一个专注于嵌入式IoT领域的架构师,深耕IoT领域多年,深度掌握IoT领域的相关技术栈,包括但不限于RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、嵌入式IoT系统的架构设计等。 10万+

linux——list.h

*linux中的头文件list.h 在Linux的源码中有一个自己为建立链表而写的链表头文件,以后可以直接调用里面的函数来使用链表。 list.h在linux中的路径为/usr/src/kernels/include/linux。不同的版本可能不一样。 list.h中有两个宏定义:#define LIST_HEAD_INIT(name) {&(name),&(name)} #define LI

sinat_34467747的博客 7516

Linux内核链表管理详解:list.h深入分析

list_headlist_entry宏是Linux内核中用于将链表节点(list_head结构)转换为其所属的数据结构实例的宏。在Linux内核中,链表节点通常被嵌入到其他数据结构中,以实现对这些数据结构的链表操作。list_entry宏提供了一种安全且便捷的方式来根据链表节点找到它所对应的完整数据结构的指针。宏的定义通常如下:在这里,ptr是一个指向list_head结构的指针,type是包含list_head的结构体类型,member是该结构体中。

weixin_29496633的博客 826

Linux内核中的list.h浅谈

[十月往昔]——Linux内核中的list.h浅谈 为什么要叫做“十月往昔”呢,是为了纪念我的原博客。 不知道为什么,突然想来一个新的开始——而那个博客存活至今刚好十个月,也有十个月里的文档。 十月往昔,总有一些觉得珍贵的,所以搬迁到这里来。 而这篇文章是在09.04.10里写的。 Jason Lee   ————————————–cut-line /*------------

JasonLee的专栏 5828

高效穿搭博主内容制作指南:DeepSeek+豆包+即梦AI+剪映黄金工作流

大家好,我是硅基打工人! 只需3步实现AI自动化生成穿搭带货图文,日均产出效率提升10倍

TuFuir的博客 2676

大麦APP抢票协议分析:从‘掌密网络’代码看移动端API安全防护

本文深度解析了大麦APP抢票协议的安全防护体系,重点分析了'掌密网络'代码中体现的动态签名、设备指纹和人机验证等关键技术。文章详细介绍了大麦APP如何通过多层次API安全防护应对抢票压力,包括参数加密、请求头部设计和异常处理机制,为移动应用开发者提供了安全实践建议。

weixin_28740299的博客 289

linux内核list.h头文件分析(七)——list.h应用

自己把list.h源文件的代码一下部分做了手脚,下次如果在你的程序当中有用到list或者是hlist的时候,你就可以把我修改之后的内容复制一下,命名为list.h,然后在你的用户程序中写着这么一句#include "list.h"这样的话,你就可以很好的对内核链表进行使用了。改动的地方(代码中的红色部分):1)#define LIST_POISON1  (void *) 0x0#define LI

sunjiangangok的博客 1839

基于结构的虚拟筛选模型 gnina 从源码编译

所需要的软件安装包: 安装 CUDA 装的时候看着点,第二个要你装驱动的选择,回答N,不装。 sudo sh cuda_10.0.130_410.48_linux.run sudo vim ~/.bashrc 在 ~/.bashrc 文件最后加上 export PATH=/usr/bin:/usr/local/cuda-10.0/bin${PATH} export LD_LIBRARY_PATH=/usr/local/cuda-10.0/lib64:${LD_LIBRARY_PATH} source ~

周迪新的博客 3187

linux内核学习中-- 史上最全 linux通用链表“list.h”详解

最近几天在学习linux内核,接触到“list.h”文件,学习了几天,在这里做一下总结。也在网上学习了很多前人的工作。好像大家的工作都比较零散,每个人都是仅仅解释了某几个函数。为了以后大家学习方便,,在这里我将所有的函数以及头文件通通解释下,算是比较全面的总结吧!。希望对大家今后的学习有用,也望大家对里面的错误和缺点指出。 下面,我开始了。  第一段,我就不多解释了,大家应该能看懂,重点在后面

ggxxkkll的专栏 9424

基于Python的闲鱼商品价格评估模型:爬虫+价格预测全实现

本文介绍了一个完整的全球气候数据抓取与分析流程,采用轻量化的技术栈(requests+pandas+matplotlib+folium),实现以下核心功能: 全球城市定向抓取:支持自定义城市列表,自动获取经纬度并抓取多源气候数据 双数据源互补:结合OpenWeatherMap API(实时数据)和Climate-Data.org(历史数据) 全维度覆盖:采集气温、降水、湿度等核心指标,并进行自动化清洗转换 丰富可视化:生成热力图、趋势图和统计图表,支持地理定位展示 灵活扩展:模块化设计便于新增城市或扩展数据

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 3046

在abaqus中利用point-based 来快捷定义螺栓连接

abaqus中可以通过point-based方式定义连接点的位置,并对连接方式进行定义,可以进行螺栓连接的模拟。 可以通过逐点输入坐标或导入外部的坐标位置 也可以通过阵列的方式定义点 定义完点后就利用create fasteners,选择point-based,选取刚才创建好的点阵列,然后定义连接的属性,如下图 定义connector section时,类型选择translator,相当于只有1方向可以动,定义弹性模量以及破坏准则。 案例操作 假如有两个板,通过...

fiber_tape的博客 7337

linux中的list.h(2)

linux内核中链表的创建linux内核中的双链表操作非常经典。为了加深对代码的理解,我将在用户态下对里面的内容进行一些实现。 注意list.h是在内核态中用的,在用户态直接#include <linux/list.h>是不行的。不过,虽然list.h是内核代码中的头文件,但我们可以把它移植到用户空间使用。相关博文: linux中的list.h(1) ——>linux内核中链表简介,list

_今已亭亭如盖矣 1575

Unlocking Decoding-time Controllability: Gradient-FreeMulti-Objective Alignment with Contrastive Pr

将基于庞大网络语料库训练的大型语言模型(LLMs)(OpenAI,2023;Touvron 等,2023;Google,2023)与人类偏好对齐,是减少产生不安全(Wei 等,2023)、幻觉(Zhang 等,2023b)和有偏见(Gallegos 等,2023)内容的重要一步。随着偏好学习技术(如 PPO(Schulman 等,2017)、DPO(Rafailov 等,2023)及其他变体(Azar 等,2023;Ethayarajh 等,2024;

qq_52053775的博客 1168

linux内核中的list详解

 1 list_entry作用就是通过list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是type型的,而ptr在其宿主结构中定义为member成员。定义在内核源文件include/linux/list.h中,对比list_entry(ptr,type,member)可知有以下结果: 其中list相当于member成员,struct example_struct相当于

hondrif82q的专栏 6422

【FF-A】第二章 Introduction

Armv8.4 架构引入了在安全状态下的虚拟化扩展。Arm® SMMU v3.2 架构[1]为安全流添加了对第2阶段翻译的支持,以补充在 Armv8.4 处理器状态下的安全 EL2 翻译制度。这些架构特性使得在安全状态下,彼此不信任的软件组件能够相互隔离。关于在安全状态下引入虚拟化扩展以及固件框架目标的更多理念可在名为《在安全世界使用虚拟化进行隔离》的白皮书[3]中找到。软件组件只能访问物理地址空间和系统资源的区域,例如 GIC 中对其正确运行必要的中断。

baron-周贺贺-代码改变世界ctw 528

【数据分析】比较SparCC、Pearson和Spearman相关性估计方法在合成组学数据上的表现

【数据分析】比较SparCC、Pearson和Spearman相关性估计方法在合成组学数据上的表现

专注生信领域 659

浅谈linux内核中的list.h[1]

 1#ifndef _LINUX_LIST_H2#define _LINUX_LIST_H34#include linux/stddef.h>5#include linux/poison.h>6#include linux/prefetch.h>7#include asm/system.h>  链表的初始化19struct list_head {20

tedious的专栏 675

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

深入理解Linux内核之链表 list.h 功能实现原理、接口说明及示例代码

目录 一、双向链表接口函数及相关宏定义分析 0、list_head 结构体 1、offsetof(TYPE, MEMBER) 宏 2、ontainer_of(ptr, type, member) 宏 3、LIST_HEAD_INIT 宏 4、LIST_HEAD 宏 5、INIT_LIST_HEAD 函数 6、LIST_HEAD 和INIT_LIST_HEAD 的区别 7、lis...

猪哥的专栏 7663

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

linux 内核list数据结构

list

weixin_43412488的博客 1594

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Linux内核中的通用链表list.h在windows下的移植实现

转自:https://blog.csdn.net/dijkstar/article/details/19249219 我这里用的是Linux2.4版本的,2.6版本的其实都一样,下面是修改后的list.h源文件,注意几点:① 注释掉了和Linux相关的字眼,如第四行、第六行等,添加了prefetch(w)两个函数的实现;② 因为是在C语言下实现(不是C++),VC6-VC2005-VC2010编...

yeyiliang的专栏 578

浅析Linux内核中的链表

当然,调用者完全可以自己缓存next指针使遍历操作能够连贯起来,但为了编程的一致性,Linux链表仍然提供了两个对应于基本遍历操作的“_safe”接口:list_for_each_safe(pos,n, head)、list_for_each_entry_safe(pos, n, head, member),它们要求调用者另外提供一个与pos同类型的指针n,在for循环中暂存pos下一个节点的地址,避免因pos节点被释放而造成的断链。与之相对应,list_del_init()函数将节点从链表。

m0_74282605的博客 9797
上一篇: 浅谈linux内核中的list.h[1]
下一篇: Linux内核源码分析-链表代码分析
tedious
博客等级 码龄21年 22粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值