第四十章 linux-并发解决方法五(顺序锁seqlock)
第四十章 linux-并发解决方法四(顺序锁seqlock)
文章目录
- 第四十章 linux-并发解决方法四(顺序锁seqlock)
顺序锁的设计思想是,对某一共享数据读取时不加锁,写的时候加锁。为了保证读取的过程中不会因为写入名的出现导致该共享数据的更新,需要在读取者和写入者之间引入一整型变量,称为顺序值sequence。读取者在开始读取前读取该sequence,在读取后再重读该值,如果与之前读取到的值不一致,则说明本次读取作过程中发生了数据更新,读取操作无效,因此要求写入者在开始写入的时候要更新sequence的值。
typedef struct {struct seqcount seqcount;spinlock_t lock;
} seqlock_t;
无符号型整数sequence用来协调读取者与写入者的作spinlock变量lock在多个写入者之间做互斥使用。
程序中如果想静态定义一个seqlock并同时初始化,可以使用DEFINESE_QLOCK宏,该宏会定义一个seqlock_t型变量并初始化其sequence为0,lock为0:
#define __SEQLOCK_UNLOCKED(lockname) \{ \.seqcount = SEQCNT_ZERO(lockname), \.lock = __SPIN_LOCK_UNLOCKED(lockname) \}
#define DEFINE_SEQLOCK(x) \seqlock_t x = __SEQLOCK_UNLOCKED(x)
如果要动态初始化一个seqlock变量,可以使用seqlock_init:
#define seqlock_init(x)
do {
seqcount_init(&(x)->seqcount);
spin_lock_init(&(x)->lock);
} while (0)
下面看看写入者在seqlock上的上锁操作write_seqlock:
static inline void write_seqlock(seqlock_t *sl)
{spin_lock(&sl->lock);write_seqcount_begin(&sl->seqcount);
}
static inline void write_seqcount_begin(seqcount_t *s)
{write_seqcount_begin_nested(s, 0);
}
static inline void write_seqcount_begin_nested(seqcount_t *s, int subclass)
{raw_write_seqcount_begin(s);seqcount_acquire(&s->dep_map, subclass, 0, _RET_IP_);
}
static inline void raw_write_seqcount_begin(seqcount_t *s)
{s->sequence++;smp_wmb();
}
写入者在对写之前需要先获得seqlock上的自旋锁lock,这说明在写入者之间必须保证互斥操作,如果某一写入者成功获得lock。那么需要史新sequence的值以便让其他写入者知道共享数据发生了更新。写入者与写入者之间并不需要sequence。
static inline void write_sequnlock(seqlock_t *sl)
{write_seqcount_end(&sl->seqcount);spin_unlock(&sl->lock);
}
static inline void write_seqcount_end(seqcount_t *s)
{seqcount_release(&s->dep_map, 1, _RET_IP_);raw_write_seqcount_end(s);
}
static inline void raw_write_seqcount_end(seqcount_t *s)
{smp_wmb();s->sequence++;
}
主要的工作是释放自旋锁lock,至于写入者对sequence的更新,主要是用来告诉读取者有数据更新发生,所以必须确保sequence的值在写入的前后发生变化。在此基础上sequence提供的另外一个信息是写入过程有没有结束,这是用sequence的最低位来完成的,如果sequence & 0为0表明写入过程己经结束,否则表明写入过程正在进行·接下来会在读取者的seqlock作数中看到sequence的这两种用途。
某一写入者可以使用write_tryseqlock来保证在无法获得lock时不让自己进入自旋状态(当然也就无法更新数据)而直接返回0,成功获得锁则返回1:
读取者在读取开始前需要先调用read_seqbegin函数,该函数主要用来返回读取开始之前的sequence值:
static inline unsigned read_seqbegin(const seqlock_t *sl)
{return read_seqcount_begin(&sl->seqcount);
}
static inline unsigned read_seqcount_begin(const seqcount_t *s)
{seqcount_lockdep_reader_access(s);return raw_read_seqcount_begin(s);
}
static inline unsigned raw_read_seqcount_begin(const seqcount_t *s)
{unsigned ret = __read_seqcount_begin(s);smp_rmb();return ret;
}
s
tatic inline unsigned __read_seqcount_begin(const seqcount_t *s)
{unsigned ret;repeat:ret = READ_ONCE(s->sequence);if (unlikely(ret & 1)) {cpu_relax();goto repeat;}return ret;
}
从函数的实现也可以看出,如果当前正好有写入者在进行写操作,那么该函数将不停循环直到写过程结束,前面曾提到sequence最低位的用途,这里正好是其实际使用的地方。另一方面,从读取者对写入过程结束的孤环等待可以看出,写入者的实际写入操作占用的时间不应太长。
内核还给读取者提供了一个read_seqretry函数,与read_seqbegin的返回值一起使用,来判定本次的读取操作是否有效:
static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
{return read_seqcount_retry(&sl->seqcount, start);
}
static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
{smp_rmb();return __read_seqcount_retry(s, start);
}
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{return unlikely(s->sequence != start);
}
函数的参数是读取者在读取操作之前调用read_seqbegin获得的初始值。如果本次读取无效(读取过程中发生了数据更新),那么readq返回1,否则返回0。下面分别给出写入者和读取者利用上面介绍的seqlock函数进行数据读/写协调的例子:
//定义一个全局的seqlock变量demo_seqlock
DEFINE_SEQLOCK(demo_seqlock);//对于写入者的代码
...
//实际写之前调用write_seqlock获取自旋锁,同时更新sequence的值
write_seqlock(&demo_seqlock);
//获得自旋锁之后调用do_write进行实际的写入操作
do_write();
//写入结束,调用write_sequnlock释放锁
write_sequnlock(&demo_seqlock);//对于读取者的代码
unsigned start;
do{//读操作前鲜活的sequence的值start,用以在读操作结束时判断数据是否发生更新//注意读操作无需获得锁start = read_seqbegin(&demo_seqlock);//调用do_read进行实际操作do_read();
}while(read_seqretry(&demo_seqlock,start));//如果数据有更新,再重新读取
如果考虑到中断安全的问题,可以使用读取者与写入者的对应版本:
void write_seqlock_irq(seqlock_t *sl)
void write_seqlock_bh(seqlock_t *sl)
write_seqlock_irqsave(lock, flags)
void read_seqlock_irq(seqlock_t *sl)
void read_seqlock_bh(seqlock_t *sl)
read_seqlock_irqsave(lock, flags
前面曾讨论过读取者与写入者自旋锁rwlock,对比这里的seqlock.会发现两者非常相似。不同之处在于seqlock在写的时候只与其他写入者互斥,而rwlock在写的时候与读取者和写入者都互斥·因此当要保护的资源很小很简单,会很频繁被访问并且写入操作很少发生且必须快速时,就可以使用seqlock。
相关文章:
第四十章 linux-并发解决方法五(顺序锁seqlock)
第四十章 linux-并发解决方法四(顺序锁seqlock) 文章目录第四十章 linux-并发解决方法四(顺序锁seqlock)顺序锁的设计思想是,对某一共享数据读取时不加锁,写的时候加锁。为了保证读取的过程中不会因为写入名…...

【SPSS】交叉设计方差分析和协方差分析详细操作教程(附案例实战)
🤵♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 目录 方差分析概述 交叉设计方差分析...
playwright--核心概念和Selector定位
文章目录前言一、浏览器二、浏览器上下文三、页面和框架四、Selectors1、data-test-id selector2、CSS and XPath selector3、text 文本selector4、id定位selector5、Selector 组合定位五、内置Selector前言 Playwright提供了一组API可自动化操作Chromium,Firefox和…...

响应式操作实战案例
Project Reactor 框架 在Spring Boot 项目 Maven 中添加依赖管理。 <dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId> </dependency><dependency><groupId>io.projectreactor</g…...

NetApp AFF A900:针对任务关键型应用程序的解决方案
NetApp AFF A900:适用于数据中心的解决方案 AFF A 系列中的 AFF A900 高端 NVMe 闪存存储功能强大、安全可靠、具有故障恢复能力,提供您为任务关键型企业级应用程序提供动力并保持数据始终可用且安全所需的一切。 AFF A900:针对任务关键型应…...

使用Houdini输出四面体网格并输出tetgen格式
我们的目标是从houdini输出生成的四面体,希望是tetgen格式的。 众所周知,houdini是不能直接输出四面体的。 有三方案去解决: 输出点云ply文件,然后利用tetgen生成网格。输出Hounidi内置的.geo格式文件,然后写个脚本…...

组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比
组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比 目录 组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 MATLAB实现EMD-KP...

【C语言】操作符详解总结(万字)
操作符详解1. 操作符分类2. 算术操作符3. 移位操作符3.1 整数的二进制是怎么形成的3.2 左移操作符3.3 右移操作符4. 位操作符5. 赋值操作符6. 单目操作符6.1 单目操作符介绍6.2 sizeof 和 数组7. 关系操作符8. 逻辑操作符9. 条件操作符9.1 练习19.2 练习210. 逗号表达式11. 下标…...

mac系统手册(帮助/说明)
文章目录1. mac自带的帮助文档2. Mac使用技巧(提示)2.1 聚焦搜索2.2 截图(录制屏幕)2.3 调出右键菜单2.4 快速查看2.5 翻译2.5.1 词典解释2.5.2 翻译(字、词和句)3. macOS使用手册3.1 在聚焦中进行计算和转…...
VLC播放器Demo(录像,截图等功能),Android播放器Demo可二次开发。
VLC播放器Demo(录像,截图等功能),可二次开发。 GitHub地址:https://github.com/ILoveLin/VlcRecordPlayer GitHub地址:https://github.com/ILoveLin/VlcRecordPlayer GitHub地址:https://github.com/ILoveLin/VlcRecordPlayer …...

WeSpeaker支持C++部署链路
WeSpeaker正式更新C部署链路,推理引擎使用OnnxRuntime,支持从语音中提取Speaker Embedding信息,代码详见WeSpeaker/runtime[1]。 Libtorch和onnx的选择? Speaker Embedding提取任务流程简单,并且声纹模型(如ResNet\E…...

window vscode编辑appsmith源码
前言 本来最开始用的idea打开wsl中的appsmith,卡得一批。最后没办法,用自己的电脑装成ubuntu server,然后vscode的远程开发对appsmith源码进行编辑。如果自己电脑内存16个G或者更大可能打开wsl中的估计会还好,我公司电脑只有8g所…...

操作系统面试题
操作系统一、简介篇1.解释一下什么是操作系统2.操作系统的主要功能3.软件访问硬件的几种方式4.操作系统的主要目的是什么5.为什么Linux系统下的应用程序不能直接在Windows下运行6.什么是用户态和内核态7.用户态和内核态如何切换8.什么是内核二、进程和线程篇1.多处理系统的优势…...

Kafka入门(七)
下面聊聊Kafka的配置参数,包括生产者的配置参数、Broker的配置参数、消费者的配置参数。 1、生产者配置参数 acks 该参数控制了生产者的消息发送确认机制,用于指定分区中必须有多少个副本成功接收到消息后生产者才会认为这条消息写入是成功的,…...

微服务介绍
微服务 微服务架构发展 微服务这个概念最早是在2011年5月威尼斯的一个软件架构会议上讨论提出的,用于描述一些作为通用架构风格的设计原则;2012年3月在波兰举行的Degree Conference大会,james lewis做演讲,讨论了微服务一些原则…...

搭建SpringBoot多模块微服务项目脚手架(三)
搭建SpringBoot多模块微服务项目脚手架(三) 文章目录搭建SpringBoot多模块微服务项目脚手架(三)1.概述项目结构2.接口返回统一信息模板2.1.封装返回统一信息思路介绍2.2.封装json数据格式1.导入依赖2.封装code码3.封装json格式模板4.使用统一返回信息3.接口统一请求信息模板3.1…...
对vue3中reactive、toref、torefs、ref的详细理解
reactive:将平常的一个对象转换成响应式对象。所谓的响应式对象就是当页面点击修改此对象时,页面无需刷新而在页面上的其他地方有用到这个对象的地方会自动同步修改过来例如: <template><div class"container"><di…...

C++ Primer Plus 第6版 读书笔记(6) 第 6 章 分支语句和逻辑运算符
第 6 章 分支语句和逻辑运算符 C是在 C 语言基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言,是C语言的超集。本书是根据2003年的ISO/ANSI C标准编写的,通过大量短小精悍的程序详细而全面地阐述了 C的基本概念和技术,…...

Java Class 加密工具 ClassFinal
Jar包加密工具 ClassFinal介绍环境依赖使用说明下载加密命令行示例maven插件方式无密码模式机器绑定启动加密后的jar启动参数给密码不加密码参数直接启动1. 密码文件获取2. 交互输入参考资料介绍 ClassFinal 是一款 java class 文件安全加密工具,支持直接加密jar包…...
【蓝桥杯集训·每日一题】AcWing 3555. 二叉树
文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴最近公共祖先一、题目 1、原题链接 3555. 二叉树 2、题目描述 给定一个 n 个结点(编号 1∼n)构成的二叉树,其根结点为 1 号点。 进行 m…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...