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");

测试

加载模块

卸载模块 

小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/9545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32(HAL库)驱动st7789LCD屏幕(7引脚240*240)

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 屏幕引脚配置 2.3 项目生成 3、KEIL端程序整合 3.1 LCD驱动添加 3.2 函数修改 3.2.1 lcd.h修改 3.2.2 lcd_innit.h 修改 3.2.3 lcd.c修改 3.2.4 lcd_inut.c修改 3.3 主函数代码 3.3…

Django学习笔记-表单(forms)的使用

在Django中提供了了form表单&#xff0c;可以更为简单的创建表单模板信息&#xff0c;简化html的表单。 一、网页应用程序中表单的应用 表单通常用来作为提交数据时候使用。 1.1 创建表单模板文件夹 在项目文件夹下创建一个template文件夹&#xff0c;用于存储所有的html模…

使用百度地图SDK计算距离

说明&#xff1a;通过百度地图提供的SDK&#xff0c;可以计算出两个地点之间的距离&#xff0c;另外还有行驶路线等等。本文介绍如果使用百度地图SDK&#xff0c;并用java代码实现。 申请 首先需要登录百度地图的官网&#xff0c;申请开发者认证&#xff0c;个人认证一般都很…

GPT一键化身「AI助理」——自定义指令功能

最近GPT又更新了一个超实用的功能——自定义指令&#xff0c;启用后&#xff0c;你可以给GPT设置一些固定指令&#xff0c;让它记住或扮演某个角色&#xff0c;比如客服、律师、投资管理师、老师、营养师...... 这样&#xff0c;我们就不再需要每次都要打开新的聊天&#xff0c…

ChatGPT的工作原理:从输入到输出

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

华为eNSP:isis配置跨区域路由

一、拓扑图 二、路由器的配置 1、配置接口IP AR1: <Huawei>system-view [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei-GigabitEthernet0/0/0]q AR2: [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.2 24 [Huawe…

【React Native】学习记录(一)——环境搭建

Expo是一套工具&#xff0c;库和服务&#xff0c;可让您通过编写JavaScript来构建原生iOS和Android应用程序。 一开始学习的时候直接使用的是expo。 npx create-expo-app my-appcd my-appnpm run start接下来需要搭建安卓和IOS端&#xff08;为此特意换成了苹果电脑&#xff09…

云曦暑期学习第二周——文件上传漏洞

1.文件上传 1.1原理 一些web应用程序中允许上传图片、视频、头像和许多其他类型的文件到服务器中。 文件上传漏洞就是利用服务端代码对文件上传路径变量过滤不严格将可执行的文件上传到一个到服务器中 &#xff0c;再通过URL去访问以执行恶意代码。 1.2为什么存在文件上传漏…

Angular:动态依赖注入和静态依赖注入

问题描述&#xff1a; 自己写的服务依赖注入到组件时候是直接在构造器内初始化的。 直到看见代码中某大哥写的 private injector: Injector 动态依赖注入和静态依赖注入 在 Angular 中&#xff0c;使用构造函数注入的方式将服务注入到组件中是一种静态依赖注入的方式。这种方…

ThinkPHP8知识详解:给PHP8和MySQL8添加到环境变量

在PHPenv安装的时候&#xff0c;环境变量默认的PHP版本是7.4的&#xff0c;MySQL的版本是5.7的&#xff0c;要想使用ThinkPHP8来开发&#xff0c;就必须修改环境变量&#xff0c;本文就详细讲解了如果修改PHP和MySQL的环境变量。 1、添加网站 启动phpenv&#xff0c;网站&…

LiveGBS流媒体平台GB/T28181功能-设备树自定义分组自定义组织机构选择通道共享给上级国标平台配置权限给指定用户

LiveGBS流媒体平设备树自定义分组自定义组织机构选择通道共享给上级国标平台权限给指定用户 1、背景2、分组2.1、新建分组2.2、选择通道2.3、导入设备2.4、编辑名称2.5、删除分组2.6、移除分组 3、国标级联3.1、分组共享节点3.1.1、共享给上级平台3.1.2、分配权限给用户 3.2、级…

Android 帧率分析

卡顿&#xff1a; 界面呈现是指从应用生成帧并将其显示在屏幕上的动作。如需确保用户能够流畅地与您的应用互动&#xff0c;您的应用呈现每帧的时间不应超过 16ms&#xff0c;以达到每秒 60 帧的呈现速度&#xff08;为什么是 60fps&#xff1f;&#xff09;。如果您的应用存在…

PV操作解决经典进程同步问题

一.经典同步问题 在学习《操作系统》时&#xff0c;会接触到进程的概念&#xff0c;其中不可避免的接触到进程同步问题&#xff0c;今天我们用熟悉的PV操作解决一些经典的进程同步问题。 二.生产者-消费者问题 1.问题描述 问题描述&#xff1a;一组生产者进程和一组消费者进…

汽车交流充电桩控制主板的电路设计

汽车充电桩控制主板的电路设计 你是否曾经遇到过汽车没油的问题?但是&#xff0c;随着电动汽车的普及&#xff0c;充电问题也变得越来越重要。而汽车充电桩控制板电路设计则是解决这一问题的关键。 汽车充电桩控制板电路设计包括硬件电路设计、软件电路设计和安全性设计。硬件…

[vulnhub]DC2

文章目录 [vulnhub]DC2信息收集flag1flag2cewlwpscan flag3什么是rbash&#xff1f; flag4flag5git提权 总结 [vulnhub]DC2 信息收集 扫ip&#xff0c;有两种方式&#xff1a;arp、nmap nmap -sP 192.168.56.0/24 -T4arp-scan -l192.168.56.137 扫端口&#xff1a; nmap -…

TEE GP(Global Platform)认证方案

TEE之GP(Global Platform)认证汇总 一、GP认证方案 二、GP认证方案分类 参考&#xff1a; GlobalPlatform Certification - GlobalPlatform

微服务系列(1)-who i am?

微服务系列&#xff08;1&#xff09;-我是谁 应用架构的演化 简单来说系统架构可以分为以下几个阶段&#xff1a;复杂的臃肿的单体架构-SOA架构-微服务 单体架构及其所面临的问题 在互联网发展初期&#xff0c;用户数量少&#xff0c;流量小&#xff0c;硬件成本高。因此…

pytorch学习-线性神经网络——softmax回归+损失函数+图片分类数据集

1.softmax回归 Softmax回归&#xff08;Softmax Regression&#xff09;是一种常见的多分类模型&#xff0c;可以用于将输入变量映射到多个类别的概率分布中。softmax回归是机器学习中非常重要并且经典的模型&#xff0c;虽然叫回归&#xff0c;实际上是一个分类问题 1.1分类与…

【Java】JUC并发编程-进程线程

目录 一、什么是JUC二、进程和线程1、进程2、线程 三、线程的六种状态四、wait与sleep的区别五、并发与并行1、串行模式2、并行模式3、并发模式4、管程 六、用户线程与守护线程1、用户线程&#xff08;自定义线程&#xff09;2、守护线程&#xff08;比如垃圾回收&#xff09; …

Python爬虫学习笔记(十三)————CrawlSpider

目录 1.CrawlSpider介绍 2.使用方法 &#xff08;1&#xff09;提取链接 &#xff08;2&#xff09;模拟使用 &#xff08;3&#xff09;提取连接 &#xff08;4&#xff09;注意事项 3.运行原理 4.Mysql 5.pymysql的使用步骤 6.数据入库 &#xff08;1&#xff09;s…