rcu链表综合实践
基础知识
rcu-read copy update的缩写。和读写锁起到相同的效果。据说牛逼一点。对于我们普通程序员,要先学会使用,再探究其内部原理。
链表的数据结构:
struct list_head {struct list_head *next, *prev;
};
还有一种:struct hlist_head,本文不做该链表的测试。
struct hlist_head {struct hlist_node *first;
};struct hlist_node {struct hlist_node *next, **pprev;
};
涉及的文件:include\linux\rculist.h
初始化链表:INIT_LIST_HEAD_RCU
/** INIT_LIST_HEAD_RCU - Initialize a list_head visible to RCU readers* @list: list to be initialized** You should instead use INIT_LIST_HEAD() for normal initialization and* cleanup tasks, when readers have no access to the list being initialized.* However, if the list being initialized is visible to readers, you* need to keep the compiler from being too mischievous.*/
static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
{WRITE_ONCE(list->next, list);WRITE_ONCE(list->prev, list);
}
添加节点list_add_rcu(插入到头节点后面)
/*** list_add_rcu - add a new entry to rcu-protected list* @new: new entry to be added* @head: list head to add it after** Insert a new entry after the specified head.* This is good for implementing stacks.** The caller must take whatever precautions are necessary* (such as holding appropriate locks) to avoid racing* with another list-mutation primitive, such as list_add_rcu()* or list_del_rcu(), running on this same list.* However, it is perfectly legal to run concurrently with* the _rcu list-traversal primitives, such as* list_for_each_entry_rcu().*/
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
{__list_add_rcu(new, head, head->next);
}
添加节点list_add_tail_rcu(插入到头节点前面,就是链尾)
/*** list_add_tail_rcu - add a new entry to rcu-protected list* @new: new entry to be added* @head: list head to add it before** Insert a new entry before the specified head.* This is useful for implementing queues.** The caller must take whatever precautions are necessary* (such as holding appropriate locks) to avoid racing* with another list-mutation primitive, such as list_add_tail_rcu()* or list_del_rcu(), running on this same list.* However, it is perfectly legal to run concurrently with* the _rcu list-traversal primitives, such as* list_for_each_entry_rcu().*/
static inline void list_add_tail_rcu(struct list_head *new,struct list_head *head)
{__list_add_rcu(new, head->prev, head);
}
删除节点list_del_rcu
/*** list_del_rcu - deletes entry from list without re-initialization* @entry: the element to delete from the list.** Note: list_empty() on entry does not return true after this,* the entry is in an undefined state. It is useful for RCU based* lockfree traversal.** In particular, it means that we can not poison the forward* pointers that may still be used for walking the list.** The caller must take whatever precautions are necessary* (such as holding appropriate locks) to avoid racing* with another list-mutation primitive, such as list_del_rcu()* or list_add_rcu(), running on this same list.* However, it is perfectly legal to run concurrently with* the _rcu list-traversal primitives, such as* list_for_each_entry_rcu().** Note that the caller is not permitted to immediately free* the newly deleted entry. Instead, either synchronize_rcu()* or call_rcu() must be used to defer freeing until an RCU* grace period has elapsed.*/
static inline void list_del_rcu(struct list_head *entry)
{__list_del_entry(entry);entry->prev = LIST_POISON2;
}
删除尾节点hlist_del_init_rcu
/*** hlist_del_init_rcu - deletes entry from hash list with re-initialization* @n: the element to delete from the hash list.** Note: list_unhashed() on the node return true after this. It is* useful for RCU based read lockfree traversal if the writer side* must know if the list entry is still hashed or already unhashed.** In particular, it means that we can not poison the forward pointers* that may still be used for walking the hash list and we can only* zero the pprev pointer so list_unhashed() will return true after* this.** The caller must take whatever precautions are necessary (such as* holding appropriate locks) to avoid racing with another* list-mutation primitive, such as hlist_add_head_rcu() or* hlist_del_rcu(), running on this same list. However, it is* perfectly legal to run concurrently with the _rcu list-traversal* primitives, such as hlist_for_each_entry_rcu().*/
static inline void hlist_del_init_rcu(struct hlist_node *n)
{if (!hlist_unhashed(n)) {__hlist_del(n);n->pprev = NULL;}
}
替换list_replace_rcu:
/*** list_replace_rcu - replace old entry by new one* @old : the element to be replaced* @new : the new element to insert** The @old entry will be replaced with the @new entry atomically.* Note: @old should not be empty.*/
static inline void list_replace_rcu(struct list_head *old,struct list_head *new)
{new->next = old->next;new->prev = old->prev;rcu_assign_pointer(list_next_rcu(new->prev), new);new->next->prev = new;old->prev = LIST_POISON2;
}
计算长度
判空:list_empty
链表尾空时,返回值为1:链表不空时返回0。
下面这段注释的大体含义就是,当你想用list_empty_rcu的时候,list_empty就足够满足需要了。
/** Why is there no list_empty_rcu()? Because list_empty() serves this* purpose. The list_empty() function fetches the RCU-protected pointer* and compares it to the address of the list head, but neither dereferences* this pointer itself nor provides this pointer to the caller. Therefore,* it is not necessary to use rcu_dereference(), so that list_empty() can* be used anywhere you would want to use a list_empty_rcu().*/
获取节点对应的数据:list_entry_rcu
/*** list_entry_rcu - get the struct for this entry* @ptr: the &struct list_head pointer.* @type: the type of the struct this is embedded in.* @member: the name of the list_head within the struct.** This primitive may safely run concurrently with the _rcu list-mutation* primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().*/
#define list_entry_rcu(ptr, type, member) \container_of(READ_ONCE(ptr), type, member)
遍历 list_for_each_entry_rcu:
/*** list_for_each_entry_rcu - iterate over rcu list of given type* @pos: the type * to use as a loop cursor.* @head: the head for your list.* @member: the name of the list_head within the struct.* @cond: optional lockdep expression if called from non-RCU protection.** This list-traversal primitive may safely run concurrently with* the _rcu list-mutation primitives such as list_add_rcu()* as long as the traversal is guarded by rcu_read_lock().*/
#define list_for_each_entry_rcu(pos, head, member, cond...) \for (__list_check_rcu(dummy, ## cond, 0), \pos = list_entry_rcu((head)->next, typeof(*pos), member); \&pos->member != (head); \pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
链表综合实验代码
测试内容包括添加、计算长度、遍历数据、删除节点、替换节点。最后在卸载函数中释放链表资源。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO#define DEBUG_INFO(format,...) \printk(KERN_ERR"%s:%d -- "format"\n",\__func__,__LINE__,##__VA_ARGS__)
#else#define DEBUG_INFO(format,...)
#endifstruct rcu_private_data{struct list_head list;
};struct my_list_node{struct list_head node;int number;
};static int list_size(struct rcu_private_data *p){struct my_list_node *pos;struct list_head *head = &p->list;int count = 0;if(list_empty(&p->list)){DEBUG_INFO("list is empty");return 0;}else{DEBUG_INFO("list is not empty");}list_for_each_entry_rcu(pos,head,node){count++;}return count;
}//遍历链表
void show_list_nodes(struct rcu_private_data *p){struct my_list_node *pos;struct list_head *head = &p->list;if(list_empty(&p->list)){DEBUG_INFO("list is empty");return;}else{DEBUG_INFO("list is not empty");}list_for_each_entry_rcu(pos,head,node){DEBUG_INFO("pos->number = %d",pos->number);}
}//清空链表
void del_list_nodes(struct rcu_private_data *p){struct my_list_node *pos;struct list_head *head = &p->list;if(list_empty(&p->list)){DEBUG_INFO("list is empty");return;}else{DEBUG_INFO("list is not empty");}list_for_each_entry_rcu(pos,head,node){DEBUG_INFO("pos->number = %d\n",pos->number);vfree(pos);}
}struct rcu_private_data *prpd;static int __init ch02_init(void){int i = 0;static struct my_list_node * new[6];struct rcu_private_data *p = (struct rcu_private_data*)vmalloc(sizeof(struct rcu_private_data));prpd = p;INIT_LIST_HEAD_RCU(&p->list);DEBUG_INFO("list_empty(&p->list) = %d",list_empty(&p->list));for(i = 0;i < 5;i++){new[i] = (struct my_list_node*)vmalloc(sizeof(struct my_list_node));INIT_LIST_HEAD_RCU(&new[i]->node);new[i]->number = i;list_add_rcu(&new[i]->node,&p->list);}DEBUG_INFO("list_size = %d",list_size(p));//添加后的结果,应该是5 4 3 2 1//遍历链表:show_list_nodes(p);//删除链表节点 new[3];list_del_rcu(&new[3]->node);vfree(new[3]);DEBUG_INFO("list_size = %d",list_size(p));//遍历链表:show_list_nodes(p);//替换一个链表节点new[5] = (struct my_list_node*)vmalloc(sizeof(struct my_list_node));INIT_LIST_HEAD_RCU(&new[5]->node);new[5]->number = i;list_replace_rcu(&new[1]->node,&new[5]->node);vfree(new[1]);//遍历链表:show_list_nodes(p);DEBUG_INFO("init");return 0;
}static void __exit ch02_exit(void){del_list_nodes(prpd);vfree(prpd);DEBUG_INFO("exit");
}module_init(ch02_init);
module_exit(ch02_exit);
MODULE_LICENSE("GPL");
测试
加载模块

卸载模块

小结
相关文章:
rcu链表综合实践
基础知识 rcu-read copy update的缩写。和读写锁起到相同的效果。据说牛逼一点。对于我们普通程序员,要先学会使用,再探究其内部原理。 链表的数据结构: struct list_head {struct list_head *next, *prev; };还有一种:struct h…...
odoo16-python框架-动作
总结 1 模型和视图的 设计之美 view_ids, view_id,view_mode 最终目的都是为了生成views, 也就是视图. 模型是死的,像男人,一成不变 视图像女人,千变万化, 姿态万千 一阴一阳之谓道,设计之美又在这里得到了体现 2 所有的动作都可以通过web界面来配置 可以通过在"设…...
微信小程序——同一控件的点击与长按事件共存的解决方案
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
selenium自动化-获取元素属性信息
在写自动化过程中我们会想验证自己的代码是否正确,比如登录之后,通过用户名或其他信息来证明你登录成功,或者点击链接后,是否会跳转新的页面。通过获取元素属性信息,可以解决我们的疑惑。 一、获取内容对象的内容信息 …...
LabVIEW开发小型减阻试验平台
LabVIEW开发小型减阻试验平台 湍流摩擦在粘性流体的阻力中起着重要作用,减少湍流摩擦是流体力学领域的热门话题之一。在油气管道的长距离流体输送中,泵站提供的几乎所有动力都用于克服流体的胫骨摩擦。在流体输送领域,船舶的蒙皮摩擦阻力占总…...
解决分类任务中数据倾斜问题
大家好,在处理文本分类任务时,基准测试流行的自然语言处理架构的性能是建立对可用选项的理解的重要步骤。在这里,本文将深入探讨与分类相关的最常见的挑战之一——数据倾斜。如果你曾经将机器学习(ML)应用于真实世界的…...
Vue3 word如何转成pdf代码实现
🙂博主:锅盖哒 🙂文章核心:word如何转换pdf 目录 1.前端部分 2.后端部分 在Vue 3中,前端无法直接将Word文档转换为PDF,因为Word文档的解析和PDF的生成通常需要在后端进行。但是,你可以通过Vu…...
fpga--流水灯
fpga流水灯的设计 思路:外部时钟频率50mhz,若要实现每隔0.5s闪烁一次,则使用内部计数器计数到24999999拉高一个周期电平,当电平被拉高的时候,进行LED灯电平的设置,每次检测到高电平,就进行一位…...
51单片机:数码管和矩阵按键
目录 一:动态数码管模块 1:介绍 2:共阴极和共阳极 A:共阴极 B:共阳极 C:转化表 3:74HC138译码器 4:74HC138译码器控制动态数码管 5:数码管显示完整代码 二:矩阵按键模块 1:介绍 2:原理图 3:矩阵按键代码 一:动态数码管模块 1:介绍 LED数码管:数码管是一种…...
Django + Xadmin 数据列表复选框显示为空,怎么修复这个问题?
问题描述: 解决方法: 后续发现的报错: 解决方案: 先根据报错信息定位到源代码: 在该文件顶部写入: from django.core import exceptions然后把: except models.FieldDoesNotExist修改为&…...
《向量数据库指南》——Milvus Cloud2.2.12 易用性,可视化,自动化大幅提升
Milvus Cloud又迎版本升级,三大新特性全力加持,易用性再上新台阶! 近期,Milvus Cloud上线了 2.2.12 版本,此次更新不仅一次性增加了支持 Restful API、召回原始向量、json_contains 函数这三大特性,还优化了 standalone 模式下的 CPU 使用、查询链路等性能,用一句话总…...
Python web实战 | 用 Flask 框架快速构建 Web 应用【实战】
概要 Python web 开发已经有了相当长的历史,从最早的 CGI 脚本到现在的全栈 Web 框架,现在已经成为了一种非常流行的方式。 Python 最早被用于 Web 开发是在 1995 年(90年代早期),当时使用 CGI 脚本编写动态 Web 页面…...
十、数据结构——链式队列
数据结构中的链式队列 目录 一、链式队列的定义 二、链式队列的实现 三、链式队列的基本操作 ①初始化 ②判空 ③入队 ④出队 ⑤获取长度 ⑥打印 四、循环队列的应用 五、总结 六、全部代码 七、结果 在数据结构中,队列(Queue)是一种常见…...
Improving Cross-Modal Retrieval with Set of Diverse Embeddings
框架图: Using Triplet Loss: Smooth-Chamfer similarity Using Log-Sum-Exp,...
物联网阀控水表计量准确度如何?
物联网阀控水表是一种新型的智能水表,它采用了先进的物联网技术,可以通过远程控制和监测水表的运行情况,实现更加精准的水量计量和费用结算。那么,物联网阀控水表的计量准确度如何呢?下面我们将从以下几个方面进行详细…...
【C语言数据结构】模拟·顺序表·总项目实现
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...
自然语言处理从入门到应用——LangChain:模型(Models)-[文本嵌入模型Ⅰ]
分类目录:《自然语言处理从入门到应用》总目录 本文将介绍如何在LangChain中使用Embedding类。Embedding类是一种与嵌入交互的类。有很多嵌入提供商,如:OpenAI、Cohere、Hugging Face等,这个类旨在为所有这些提供一个标准接口。 …...
使用Gradio构建生成式AI应用程序; Stability AI推出Stable Diffusion XL 1.0
🦉 AI新闻 🚀 Stability AI推出最先进的AI工具Stable Diffusion XL 1.0 摘要:Stability AI宣布推出Stable Diffusion XL 1.0,该版本是其迄今为止最先进的AI工具。Stable Diffusion XL 1.0提供更鲜艳、更准确的图片生成ÿ…...
Java 递归计算斐波那契数列指定位置上的数字
Java 递归计算斐波那契数列指定位置上的数字 一、原理二、代码实现三、运行结果 一、原理 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多斐波那契(Leonardo Fibonacci)以兔子繁殖为…...
ai数字人透明屏的应用场景有哪些?
AI数字人透明屏的应用场景: 银行、保险、售楼处等接待场景:AI数字人透明屏可以作为接待员,提供详细的信息和导航,提高客户体验和服务效率。 商业街、购物中心等场所:AI数字人透明屏可以作为导购员,提供商品…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
