第三十九章 linux-并发解决方法二(互斥锁mutex)
第三十九章 linux-并发解决方法二(互斥锁mutex)
文章目录
- 第三十九章 linux-并发解决方法二(互斥锁mutex)
- 互斥锁的定义与初始化
- 互斥锁的DOWN操作
- 互斥锁的UP操作
用count=1的信号量实现的互斥方法还不是Linux下经典的用法,Linux内核针对count=1的信号量重新定义了一个新的数据结构,一般都称其为互斥锁或者互斥体。同时内核根据使用场景的不同,把用于信号量的DOWN和UP操作在struct mutex上作了优化与扩展,专门用于这种新的数据类型。
互斥锁的定义与初始化
互斥锁mutex的概念本来就来自semaphore,如果去除掉那些跟调试相关的成员,struct mutex和struct semaphore并没有本质的不同:
struct mutex {/* 1: unlocked, 0: locked, negative: locked, possible waiters */atomic_t count;spinlock_t wait_lock;struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)struct task_struct *owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNERstruct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXESvoid *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_map dep_map;
#endif
};
如同struct semaphore一样,对struct mutex的初始化不能直接通过操作其成员变量的方式进行,而应该利用内核提供的宏或者函数定义一个静态的struct mutex变量同时初始化的方法是利用内核的DEFINE_MUTEX:
#define __MUTEX_INITIALIZER(lockname) \{ .count = ATOMIC_INIT(1) \, .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \, .wait_list = LIST_HEAD_INIT(lockname.wait_list) \__DEBUG_MUTEX_INITIALIZER(lockname) \__DEP_MAP_MUTEX_INITIALIZER(lockname) }#define DEFINE_MUTEX(mutexname) \struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
如果在程序执行期间要初始化一个mutex变量,则可以使用mutex_init宏。去除掉那些与调试相关的操作之后,mutex_init宏可以展开成如下的函数定义形式:
# define mutex_init(mutex) \
do { \static struct lock_class_key __key; \\__mutex_init((mutex), #mutex, &__key); \
} while (0)void
__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
{atomic_set(&lock->count, 1);spin_lock_init(&lock->wait_lock);INIT_LIST_HEAD(&lock->wait_list);mutex_clear_owner(lock);
#ifdef CONFIG_MUTEX_SPIN_ON_OWNERosq_lock_init(&lock->osq);
#endifdebug_mutex_init(lock, name, key);
}
互斥锁的DOWN操作
互斥锁mutex上的DOWN操作在Linux内核中为mutex_lock函数,定义如下:
void __sched mutex_lock(struct mutex *lock)
{might_sleep();/** The locking fastpath is the 1->0 transition from* 'unlocked' into 'locked' state.*/__mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);mutex_set_owner(lock);
}
函数的设计思想体现在__mutex_fastpath_lock和__mutex_lock_slowpath两条主线上,__mutex_fastpath_lock用来快速判断当前可否获得互斥锁,如果成功获得锁,则函数直接返回,否则进入到__mutex_lock_slowpath函数中·这种设计是基于这样一个事实:想要获得某一互斥锁的代码绝大部分时候都可以成功获得。由此延伸开来在代码层面就是,mutex_lock函数进入__mutex_lock_slowpath的概率很低。
__mutex_fastpath_lock是一平台相关函数,下面以ARM处理器为例,分析其代码实现:
static inline void
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{int __done, __res;__asm__ __volatile__ (L1 "movli.l @%2, %0 \n""add #-1, %0 \n""movco.l %0, @%2 \n""movt %1 \n": "=&z" (__res), "=&r" (__done): "r" (&(count)->counter): "t");if (unlikely(!__done || __res != 0))fail_fn(count);
}
函数在百处通过ldrex完成“__res=count->counter,L2处完成__res=__res-1,L3处试图用__res的当前值来更新count->counter.这里说“试图”是因为这个更新的操作未必会成功,主要是考虑到可能有别的进程也在操作count->counter,为不使这种可能的竞争引起对
作count->counter值更新的混乱,这里用了ARM指令中用于实现互斥访问的指令ldrex和strex(前面在spinlock的代码分析时己经提过)。ldrex和strex保证了对count->counter读取一更新一写回”操作序列的原子性。如果L3处的更新操作成功,那么_ex_flag将为0。
接下来在__res|=__ex_flag执行完之后,通过if语句判断__res是否为0,有两种情况会导致__res不为0:一是在调用这个函数前count->counter=0,表明互斥锁己经被别的进程获得,
这样L2处的__res=-1:二是在L3处的更新操作不成功,表明当前有另外一个进程也在对count->counter进行同样的操作·这两种情况都将导致__mutex_fastpath_lock不能直接返回,而是进入fail_fn也就是调用__mutex_lock_slowpath。
此处if语句中的unlikely是利用GCC编译优化扩展的一个宏,这里的意思是条件语句__res!=0为真的可能性很小,编译器借此可以调整一些编译后代码的顺序达到某种程度的优化。与之对应的是likely。
如果__mutex_fastpath_lock函数不能在第一时间获得互斥锁返回,那么将进入__mutex_lock_slowpath,正如其名字所预示的那样,代码将进入一段艰难坎坷的旅途。
在Linux源码中,__mutex_lock_slowpath函数与信号量DOWN操作中的函数非常相似,不过__mutex_lock_slowpath在把当前进程放入mutex的wait_list之前会试图多次询问mutex中的count是否为1,也就是说当前进程在进入wait_list之前会多次考察别的进程是否己经释放了这个互斥锁。这主要基于这样一个事实:拥有互斥锁的进程总是会在尽可能短的时间里释放·如果别的进程己经释放了该互斥锁·那么当前进程将可以获得该互斥锁而没有必要再去睡眠。
互斥锁的UP操作
互斥锁的操作为mutex_unlock,函数定义如下:
void __sched mutex_unlock(struct mutex *lock)
{/** The unlocking fastpath is the 0->1 transition from 'locked'* into 'unlocked' state:*/
#ifndef CONFIG_DEBUG_MUTEXES/** When debugging is enabled we must not clear the owner before time,* the slow path will always be taken, and that clears the owner field* after verifying that it was indeed current.*/mutex_clear_owner(lock);
#endif__mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
}
和mutex_lock函数一样,mutex_unlock函数也有两条主线:__mutex_fastpath_unlock和__mutex_unlock_slowpath,分别用于对互斥锁的快速和慢速解锁操作。
static inline void
__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
{int __done, __res;__asm__ __volatile__ ("movli.l @%2, %0 \n\t""add #1, %0 \n\t""movco.l %0, @%2 \n\t""movt %1 \n\t": "=&z" (__res), "=&r" (__done): "r" (&(count)->counter): "t");if (unlikely(!__done || __res <= 0))fail_fn(count);
}
这里除了是将count->counter的值加1以外,代码和__mutex_fastpath_lock中的几乎完全一样。在最后的if语句中,导致代码count->counter不为0也有两种情况:一是在调用这个函数前count->counter不为0,表明在当前进程占有互斥锁期间有别的进程竞争该互斥锁:二是对count->counter的更新操作不成功,表明当前有另外一个进程也在对count->counter进行操作,这种情况主要是针对别的进程此时调用mutex1k函数导致的竞争,因为互斥的原因别的进程此时不可能调用mutex_lock。这种情况的处理是非常重要的,不只是关系到count->counter正确更新的问题,还涉及能否防止一个唤醒操作的丢失。
在没有别的进程竞争该互斥锁的情况下,__mutex_fastpath_unlock函数要完成的工作最简单,把count->counter的值加1然后返回·如果有别的进程在竞争该互斥锁,那么函数进入__mutex_unlock_slowpath这个函数主要用来唤醒在当前mutex的wait_list中休眠的进程,如同up函数一样。
相关文章:
第三十九章 linux-并发解决方法二(互斥锁mutex)
第三十九章 linux-并发解决方法二(互斥锁mutex) 文章目录第三十九章 linux-并发解决方法二(互斥锁mutex)互斥锁的定义与初始化互斥锁的DOWN操作互斥锁的UP操作用count1的信号量实现的互斥方法还不是Linux下经典的用法,…...
脚本方式本地仓库jar包批量导入maven私服
脚本内容,将以下内容保存为mavenimport.sh,放置于需要上传的目录下,可以是顶层目录,或者某个分包的目录,若私服已有待上传的包,则执行会被替换 #!/bin/bash # copy and run this script to the root of th…...
【c++】引用的学习
引用的定义和声明 引用是一种别名,它允许使用与原变量相同的内存位置。在C中,引用是使用&符号来定义的。引用必须在定义时初始化,并且可以与原变量分别使用。 int a 10; int& b a; // 定义了一个引用b,它指向a引用的作用…...
linux 软件安装及卸载
1.联网在线安装及卸载ubuntu环境下:使用apt-get 工具apt-get install - 安装软件包apt-get remove - 移除(卸载)软件包CentOS环境下:使用yum工具 (银河麒麟系统属于centos)yum install - 安装软件包yum rem…...
XShell连接ubuntu20.04.LTS
1 下载XshellXShell官方下载地址打开XSHELL官方下载地址,我们可以选择【家庭和学校用户的免费许可证】,输入邮箱之后即可获得下载链接安装非常简单,跟着提示进行即可。2 连接ubuntu2.1 查看ubuntu的ip地址输入命令查看ip地址ifconfig刚开始可…...
【FPGA】Verilog:MSI/LSI 组合电路之解码器 | 多路分解器
写在前面:本章将理解编码器与解码器、多路复用器与多路分解器的概念,通过使用 Verilog 实现多样的解码器与多路分解器,通过 FPGA 并使用 Verilog 实现。 Ⅰ. 前置知识 0x00 解码器与编码器(Decoder / Encoder) 解码器…...
深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架
文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理(1)getProxyClass0方法(2)总结写在后面一、动手实现一个动态代理框架 1、初识javassist Jav…...
一、策略模式的使用
1、策略模式定义: 策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距…...
Verilog使用always块实现时序逻辑
这篇文章将讨论 verilog 中一个重要的结构---- always 块(always block)。verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反,时序电路电路使用时钟并一定需要触发器等存储元件。因此,…...
面向对象设计模式:行为型模式之迭代器模式
一、迭代器模式,Iterator Pattern aka:Cursor Pattern 1.1 Intent 意图 Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 提供一种按顺序访问聚合对象的元素而不公开其基…...
如何快速在企业网盘中找到想要的文件
现在越来越多的企业采用企业网盘来存储文档和资料,而且现在市面上的企业网盘各种各样。在使用企业网盘过程中,很多用户会问到企业网盘中如何快速搜索文件的问题。但是无论是“标签”功能还是普通的“关键词搜索”功能,都是单层级的࿰…...
香橙派5使用NPU加速yolov5的实时视频推理(二)
三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了,我的Ubuntu系统中已经下载好了anaconda,使用anaconda的好处就是可以方便的安装一些库,而且还可以利用conda来配置虚拟环境,做到环境与环境之间相互独立。…...
算法练习-二分查找(一)
算法练习-二分查找 1 代码实现 1.1 非递归实现 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low < high) {int mid (low high) / 2;if (a[mid] value) {return mid;} else if (a[mid] < value) {low mid 1} else {high …...
通用业务平台设计(五):预警平台建设
前言 在上家公司,随着业务的不断拓展(从支持单个国家单个主体演变成支持多个国家多个主体),对预警的诉求越来越紧迫;如何保障业务的稳定性那?预警可以帮我们提前甄别风险,从而让我们可以在风险来临前将其消灭ÿ…...
Windows openssl-1.1.1d vs2017编译
工具: 1. perl(https://strawberryperl.com/) 2. nasm(https://nasm.us/) 3. openssl源码(https://www.openssl.org/) 可以自己去下载 或者我的网盘提供下载: 链接:…...
【深蓝学院】手写VIO第2章--IMU传感器--笔记
0. 内容 1. 旋转运动学 角速度的推导: 左ω∧\omega^{\wedge}ω∧,而ω\omegaω是在z轴方向运动,θ′[0,0,1]T\theta^{\prime}[0,0,1]^Tθ′[0,0,1]T 两边取模后得到结论: 线速度大小半径 * 角速度大小 其中,对旋转矩…...
网络基础(二)之HTTP与HTTPS
应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面的信息…...
Python每日一练(20230306)
目录 1. 翻转二叉树 ★★ 2. 最长公共前缀 ★★ 3. 2的幂 ★ 1. 翻转二叉树 翻转一棵二叉树。 示例 1: 输入: 4/ \2 7/ \ / \ 1 3 6 9 输出: 4/ \7 2/ \ / \ 9 6 3 1示例 2: 输入: 1…...
C/C++每日一练(20230305)
目录 1. 整数分解 ☆ 2. 二叉树的最小深度 ★★ 3. 找x ★★ 1. 整数分解 输入一个正整数,将其按7进制位分解为各乘式的累加和。 示例 1: 输入:49 输出:497^2示例 2: 输入:720 输出:720…...
SAS字典的应用
数据字典中常用信息检索DICTIONARY.COLUMNS、DICTIONARY.TABLES以及DICTIONARY.MEMBERS等字典表的内容。在编程实践中,如何以SAS字典表来提高效率。 1、DICTIONARY.COLUMNS 对于当前SAS任务的全部数据集,表格DICTIONARY.COLUMNS包含了诸如变量的名称、类…...
tao-8k Embedding模型部署教程:支持中文长文本的高兼容性向量服务
tao-8k Embedding模型部署教程:支持中文长文本的高兼容性向量服务 你是不是遇到过这样的问题?想把一段很长的中文文档,比如一篇技术报告、一份产品说明书,甚至是一本小说的章节,转换成计算机能理解的向量,…...
避坑指南:在YOLOv5-7.0中融合BiFPN时,如何平衡P2检测头带来的精度与速度损耗?
YOLOv5-7.0中BiFPN与P2检测头的精度与速度平衡实战 当你在无人机航拍画面中寻找几毫米大小的电子元件时,或者在显微镜图像中定位细胞核位置时,传统目标检测模型的性能往往会大打折扣。这正是微小目标检测技术大显身手的场景——而YOLOv5作为工业界最受欢…...
Dash.js终极指南:5分钟掌握专业级流媒体播放技术
Dash.js终极指南:5分钟掌握专业级流媒体播放技术 【免费下载链接】dash.js A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers. 项目地址: https://gitcode.com/gh_mirrors/da/dash.js Dash.js是一个…...
PyTorch实战:用门控卷积(GConv)和转置门控卷积(TrGConv)搞定音频降噪(附完整代码)
PyTorch实战:用门控卷积(GConv)和转置门控卷积(TrGConv)构建高效音频降噪模型 音频降噪一直是信号处理领域的核心挑战之一。想象一下,你正在录制一段重要的语音备忘录,背景中却充斥着风扇的嗡嗡…...
Arcgis林业资源管理实战:从GPS打点到小班成图的完整工作流
ArcGIS林业资源管理实战:从GPS打点到小班成图的完整工作流 林业资源调查是森林经营管理的基石,而GIS技术正在彻底改变传统林业调查的工作模式。记得去年参与某林场资源普查时,我们团队用传统方法完成一个林班调查需要两周,而采用A…...
OpenClaw压力测试:Phi-3-mini-128k-instruct持续运行24小时稳定性报告
OpenClaw压力测试:Phi-3-mini-128k-instruct持续运行24小时稳定性报告 1. 测试背景与目标 上周在本地部署了OpenClawPhi-3-mini组合后,我一直在思考这套方案的稳定性边界。作为个人自动化助手,它能否胜任724小时不间断工作?当我…...
企业AI定制开发:以工业场景为核心,赋能全行业数智化转型
在人工智能与实体经济深度融合的趋势下,标准化AI产品难以适配企业差异化业务流程,定制化AI开发成为企业数智化转型的关键路径。山东向量空间人工智能科技有限公司依托JBoltAI企业级Java AI应用开发框架,聚焦工业领域AI改造,同时为…...
LeetCode 热题100——3.无重复字符的最长子串
题目: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab&qu…...
竞赛获奖保研加分测评:除了挑战杯,哪些垂直赛事含金量更高?
在 2026 年推免(保研)竞争进入白热化的背景下,工科学子的加分项已不仅仅是绩点的博弈,更是工程实战能力的短兵相接。随着教育部《关于加强新时代卓越工程师培养的指导意见》的深入实施,各大名校对人才的评价标准正从“…...
1220亿美元!OpenAI创下史上最大融资纪录;DeepSeek连续三天发生服务异常;Claude Code 51万行源码泄露 | 极客头条
「极客头条」—— 技术人员的新闻圈!CSDN 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:zhanghycsdn.net)整理 | 苏宓出品 | CSDN(ID&…...
