读写锁(arm)
参考文章读写锁 - ARM汇编同步机制实例(四)_汇编 prefetchw-CSDN博客
读写锁允许多个执行流并发访问临界区。但是写访问是独占的。适用于读多写少的场景
另外好像有些还区分了读优先和写优先
读写锁定义
typedef struct {arch_rwlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAKunsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCKunsigned int magic, owner_cpu;void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;
#endif
} rwlock_t;typedef struct {u32 lock;
} arch_rwlock_t;
可以看到在arm上面读写锁其实就是一个u32的变量。通过这个变量的值能够知道读者和写者的情况。(arm上面有strex指令能够实现独占访问)
读加锁
read_lock->_raw_read_lock->__raw_read_lock->do_raw_read_lock->arch_read_lock
可以看到就是不断的用指令strex去改写这个值,直到修改成功。
读加锁简单的理解就是执行rw->lock++。
static inline void arch_read_lock(arch_rwlock_t *rw)
{unsigned long tmp, tmp2;
/* 指令strex https://blog.csdn.net/w906787/article/details/78907067 指令条件pl(非负)https://blog.csdn.net/m0_73649248/article/details/132796539rsb及常见指令 https://blog.csdn.net/Tong89_xi/article/details/103458289wfe https://blog.csdn.net/xy010902100449/article/details/126812552
*/prefetchw(&rw->lock);__asm__ __volatile__(
"1: ldrex %0, [%2]\n" // ldrex tmp, *(&rw->lock) 获取lock的值并保存在tmp中
" adds %0, %0, #1\n" // adds tmp, tmp, #1 tmp = tmp + 1 //难道是被当做一个有符号数看的,0x80000000其实是个负数??
" strexpl %1, %0, [%2]\n" // strexpl tmp2, tmp, *(&rw->lock) rw->lock = tmp, strex能独占访问,赋值成功tmp2会设置为0,反之为1WFE("mi") // wfemi (CPSR NZCV )负数就进入低功耗模式,睡眠//需要特定的事件触发才能被唤醒
" rsbpls %0, %1, #0\n" // rsbpls tmp, tmp2, #0 tmp = 0 - tmp2 运算结果会影响到cpsr寄存器。如果cpsr中N为1(感觉这里还是adds如果为负数),则执行减法,并且修改cpsr寄存器
" bmi 1b" // bmi 1b 如果strex执行成功 tmp = 0 - tmp2(0) = 0,为0 bmi不执行: "=&r" (tmp), "=&r" (tmp2): "r" (&rw->lock): "cc");smp_mb();
}
之前一直不理解adds为什么会出现负数的情况。感觉确实是把相加的结果看做是有符号的,即如果加出来的值,最高位为1,cpsr的n就会被置为1
" adds %0, %0, #1\n" // adds tmp, tmp, #1 tmp = tmp + 1 //难道是被当做一个有符号数看的,0x80000000其实是个负数??
" strexpl %1, %0, [%2]\n" // strexpl tmp2, tmp, *(&rw->lock) rw->lock = tmp, strex能独占访问,赋值成功tmp2会设置为0,反之为1
测试样例
int test_thread(void* a)
{printk(KERN_EMERG "\r\n thread start\n");
#ifdef CONFIG_PREEMPT_COUNTprintk(KERN_EMERG "\r\n CONFIG_PREEMPT_COUNT\n");
#elseprintk(KERN_EMERG "\r\n not define CONFIG_PREEMPT_COUNT\n");
#endif unsigned int cpsr = 0;unsigned long tmp = 0;unsigned long tmp2 = 0x80000000;__asm__ __volatile__("mrs %0, cpsr\n" //rw->lock = 0;:: "r" (cpsr): "cc");printk(KERN_EMERG "\r\n cpsr 0x%lx\n", cpsr);__asm__ __volatile__("adds %0, %1, #1\n": "=&r" (tmp): "r" (tmp2): "cc");__asm__ __volatile__("mrs %0, cpsr\n" //rw->lock = 0;:: "r" (cpsr): "cc");printk(KERN_EMERG "\r\n after cpsr 0x%lx, tmp 0x%lx\n", cpsr, tmp);printk(KERN_EMERG "\r\n thread end\n");return 0;
}
可以看到经过tmp = tmp2 + 1后cpsr的最高位(N)确实被置为了1

那结合后面写加锁,就能看到,如果有写者加了锁,读者是无法成功加锁的(strexpl 需要tmp非负才执行)。但是如果存在读者的情况下,其他读者继续尝试加锁是可以成功的。这样就运行多个读者进行临界区
读解锁
其实就是rw->lock--。最后需要注意的是如果tmp为0,即没有读者的时候,需要唤醒因为获取写锁失败的cpu(dsb_sev)
If the Event Register is not set, WFE(这个并不会让出cpu) suspends execution until
one ofthe following events occurs:1、an IRQ interrupt, unless masked by the CPSR I-bit
2、an FIQ interrupt, unless masked by the CPSR F-bit
3、an Imprecise Data abort, unless masked by the CPSR A-bit
4、a Debug Entry request, if Debug is enabled
5、an Event signaled by another processor using the SEV instruction.
————————————————
版权声明:本文为CSDN博主「狂奔的乌龟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xy010902100449/article/details/126812552
static inline void arch_read_unlock(arch_rwlock_t *rw)
{unsigned long tmp, tmp2;smp_mb();prefetchw(&rw->lock);__asm__ __volatile__(
"1: ldrex %0, [%2]\n" //tmp = rw->lock
" sub %0, %0, #1\n" //tmp = tmp - 1
" strex %1, %0, [%2]\n" //rw->lock = tmp
" teq %1, #0\n" //检查指令是否执行成功
" bne 1b": "=&r" (tmp), "=&r" (tmp2): "r" (&rw->lock): "cc");if (tmp == 0)dsb_sev();//唤醒获取锁失败的cpu(wfe需要sev事件唤醒)
}
写加锁
write_lock->_raw_write_lock->__raw_write_lock->do_raw_write_lock
1、感觉这里是写者加锁,需要等待全部读者退出才行 。并且这个时候写者是没有加锁成功的(即lock的值没有成功赋值为0x80000000)。read_lock那里不会出现相加为负数的情况。读者一直能够加锁成功。即只有等到读者全部退出,才能加锁成功。如果在你尝试加锁的时候,后面又来了很多加读锁的情况,你也无法阻止。只能看着读锁加锁成功
2、rw->lock = 0x80000000
static inline void arch_write_lock(arch_rwlock_t *rw)
{unsigned long tmp;prefetchw(&rw->lock);__asm__ __volatile__(
"1: ldrex %0, [%1]\n" //tmp = rw->lock
" teq %0, #0\n" //tmp == 0,需要读者全部退出才行WFE("ne") //不为0休眠,等待特定事件发生后唤醒
" strexeq %0, %2, [%1]\n" // rw->lock = 0x80000000
" teq %0, #0\n" // %0保存strex执行结果,0表示成功, 1表示失败
" bne 1b" //执行失败,则重复上述流程: "=&r" (tmp): "r" (&rw->lock), "r" (0x80000000): "cc");smp_mb();
}
写解锁
比较简单就是rw->lock = 0,然后唤醒其他获取锁失败的cpu
static inline void arch_write_unlock(arch_rwlock_t *rw)
{smp_mb();__asm__ __volatile__("str %1, [%0]\n" //rw->lock = 0;:: "r" (&rw->lock), "r" (0): "cc");dsb_sev();//这里估计是唤醒获取锁失败的cpu
}
总结
(1)假设临界区内没有任何的thread,这时候任何read thread或者write thread可以进入,但是只能是其一。
(2)假设临界区内有一个read thread,这时候新来的read thread可以任意进入,但是write thread不可以进入
(3)假设临界区内有一个write thread,这时候任何的read thread或者write thread都不可以进入
(4)假设临界区内有一个或者多个read thread,write thread当然不可以进入临界区,但是该write thread也无法阻止后续read thread的进入,他要一直等到临界区一个read thread也没有的时候,才可以进入,多么可怜的write thread。
————————————————
版权声明:本文为CSDN博主「生活需要深度」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012294613/article/details/123905288
相关文章:
读写锁(arm)
参考文章读写锁 - ARM汇编同步机制实例(四)_汇编 prefetchw-CSDN博客 读写锁允许多个执行流并发访问临界区。但是写访问是独占的。适用于读多写少的场景 另外好像有些还区分了读优先和写优先 读写锁定义 typedef struct {arch_rwlock_t raw_lock; #if…...
【第33例】IPD体系进阶:市场细分
目录 内容简介 市场细分原因 市场细分主要活动 市场细分流程 作者简介 内容简介 这节内容主要来谈谈 IPD 市场管理篇的市场细分步骤。 其中,市场管理(Market Management)是一套系统的方法。 用于对广泛的机会进行选择性收缩,...
response 拦截器返回的二进制文档(同步下载excel)如何配置
response 拦截器返回的二进制文档(同步下载excel)如何配置 一、返回效果图二、response如何配置 一、返回效果图 二、response如何配置 service.interceptors.response.use(response > {// 导出excel接口if (response.config.isExport) {return resp…...
为什么要使用云原生数据库?云原生数据库具体有哪些功能?
相比于托管型关系型数据库,云原生数据库极大地提高了MySQL数据库的上限能力,是云数据库划代的产品;云原生数据库最早的产品是AWS的 Aurora。AWS Aurora提出来的 The log is the database的理念,实现存储计算分离,把大量…...
05- OpenCV:图像操作和图像混合
目录 一、图像操作 1、读写图像 2、读写像素 3、修改像素值 4、Vec3b与Vec3F 5、相关的代码演示 二、图像混合 1、理论-线性混合操作 2、相关API(addWeighted) 3、代码演示(完整的例子) 一、图像操作 1、读写图像 (1)…...
人脸识别(Java实现的)
虹软人脸识别: 虹软人脸识别的地址:虹软视觉开放平台—以免费人脸识别技术为核心的人脸识别算法开放平台 依赖包: 依赖包是从虹软开发平台下载的 在项目中引入这个依赖包 pom.xml <!-- 人脸识别 --><dependency><gr…...
Maven 依赖管理项目构建工具 教程
Maven依赖管理项目构建工具 此文档为 尚硅谷 B站maven视频学习文档,由官方文档搬运而来,仅用来当作学习笔记用途,侵删。 另:原maven教程短而精,值得推荐,下附教程链接。 atguigu 23年Maven教程 目录 文章目…...
供应链+低代码,实现数字化【共赢链】转型新策略
在深入探讨之前,让我们首先明确供应链的基本定义。供应链可以被理解为一个由采购、生产、物流配送等环节组成的网状系统,它始于原材料的采购,经过生产加工,最终通过分销和零售环节到达消费者手中。 而数字化供应链,则是…...
[力扣 Hot100]Day3 最长连续序列
题目描述 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 出处 思路 此题可用带排序的哈希表,先构建哈希表࿰…...
【办公技巧】Word功能区灰色显示不能编辑,怎么破?
Word文档可以设置加密来保护文件禁止修改,但是在word文档中设置限制编辑功能时对它的作用是否有详细的了解呢?今天为大家介绍word限制编辑功能的作用以及忘记了限制编辑密码该如何解决。 设置限制大家应该都清楚,就是点击工具栏中的审阅 – …...
全志V853开发板原理图
本章节将对开发板几个主要的部件的原理图进行说明,方便快速上手开发板的硬件资料。 开发板硬件框图如下: 模块介绍 GPIO 分配 此表格为 V853 部分重要的 GPIO 的分配表,> 表示对IO的另外一个复用,完整的 GPIO 分配请参阅原理…...
【解决】Unity Project 面板资源显示丢失的异常问题处理
开发平台:Unity 2021.3.7f1c1 一、问题描述 在开发过程中,遭遇 Project 面板资源显示丢失、不全的问题。但 Unity Console 并未发出错误提示。 二、解决方案:删除 Library 目录 前往 “工程目录/Library” 删除内部所有文件并重打开该…...
Hyperledger Fabric Docker 方式多机部署生产网络
规划网络拓扑 3 个 orderer 节点;组织 org1 , org1 下有两个 peer 节点, peer0 和 peer1; 组织 org2 , org2 下有两个 peer 节点, peer0 和 peer1; 因为我只有 3 台虚拟机资源所以没法实现完全的多机部署,资源使用规划如下&#…...
高效降压控制器FP7132XR:为高亮度LED提供稳定可靠的电源
目录 一. FP7132概述 二. 驱动电路:FP7132 三. FP7132应用 高亮度LED作为新一代照明技术的代表,已经广泛应用于各种领域。然而,高亮度LED的工作电压较低,需要一个高效降压控制器来为其提供稳定可靠的电源。在众多降压控制器…...
Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent
文章目录 Pre概述Code源码分析 Pre Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent 概述 Spring Boot 的广播机制是基于观察者模式实现的,它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦&#…...
华为HCIE课堂笔记第十三章 IPv6地址配置
目录 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动配置 13.1.1 RS和RA报文格式 13.1.2 RA的Flags字段 13.1.3 地址的生存周期 13.1.4 RA报文中前缀中的Flags 13.2 DHCPv6 13.2.1 DHCPV6的概念 13.2.2 DCHPv6的报文 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动…...
计算机网络-VLAN间通信
之前复习了VLAN的概念以及几个接口类型。VLAN在二层可以实现广播域的划分,VLAN间可以实现二层通信,但是不能实现三层通信,需要借助其它方式。 一、概述 实际网络部署中一般会将不同IP地址段划分到不同的VLAN。同VLAN且同网段的PC之间可直接进…...
vue3的福音框架arco.design
前言: 在vue2于2023年底正式宣布不在维护,vue3使用越来越频繁的时刻,我们实现项目的辅助框架也越来越多。element, iview, antd 等经典框架继续风靡一时,不过也有很多好的框架,功能也强大,比如我们今天说的…...
BSP视频教程第29期:J1939协议栈CAN总线专题,源码框架,执行流程和应用实战解析,面向车通讯,充电桩,模组通信等(2024-01-08)
视频教程汇总帖:【学以致用,授人以渔】2024视频教程汇总,DSP第12期,ThreadX第9期,BSP驱动第29期,USB实战第5期,GUI实战第3期(2024-01-08) - STM32F429 - 硬汉嵌入式论坛 …...
Java lambda表达式如何自定义一个toList Collector
匿名类: package l8;import java.util.*; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.s…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...
