linux -- 并发 -- 并发来源与简单的解决并发的手段

互斥与同步

当多个执行路径并发执行时,确保对共享资源的访问安全是驱动程序员不得不面对的问题
互斥:对资源的排他性访问
同步:对进程执行的先后顺序做出妥善的安排

一些概念:
临界区:对共享的资源进行访问的代码片段称为临界区
并发源:导致出现多个执行路径的因素称为并发源

本节首要目标:

  • 考察linux内核中并发执行的来源
  • 讨论内核为资源互斥访问所提供的设施的幕后机制以及各自的应用场景

并发来源

可能导致对共享资源的访问出现竞争状态的若干执行路径都可以被称为并发,不一定是严格的时间意义上的并发执行才算并发的来源

并发来源:

  1. 中断处理路径:系统正在执行当前进程时,发生了中断,中断处理函数和被中断的进程之间形成的并发。软中断的执行也可以归结在此
  2. 调度器的可抢占性:单处理器上,调度器的可抢占性导致的进程与进程之间的并发,类似地,多处理器上进程与进程之间的并发。
  3. 多处理器的并发执行:多处理器的并发执行

local_irq_enable与local_irq_disable

这两个宏主要是为了解决单处理器不可抢占的调度器上的并发竞争问题,在进入临界区时local_irq_disable关闭中断,退出临界区时调用local_irq_enable来打开中断,保证临界区中系统不会出现异步的并发源。
驱动中应该避免使用这两个宏,但spinlock等互斥机制中常常使用这两个宏。

源码:

#define local_irq_enable() \
	do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0)
#define local_irq_disable() \
	do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0)

抛开两个trace前缀的调试接口不谈,raw前缀的两个接口才是此处的主角,他们是arch-specific的,不同的处理器体系结构会有不同指令来开启和关闭处理响应外部中断的能力:x86使用sti和cli,arm使用CPSIE。都旨在置位或者清除当前处理器状态寄存器这类寄存器上的中断使能位,这个flag一般决定了整个处理器能否接收中断。

此处源码为arm体系下的实现

// /include/linux/irqflags.h
/*
 * Wrap the arch provided IRQ routines to provide appropriate checks.
 */
#define raw_local_irq_disable()		arch_local_irq_disable()
#define raw_local_irq_enable()		arch_local_irq_enable()

// /arch/arm/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
	asm volatile(
		"	cpsie i			@ arch_local_irq_enable"
		:
		:
		: "memory", "cc");
}

static inline void arch_local_irq_disable(void)
{
	asm volatile(
		"	cpsid i			@ arch_local_irq_disable"
		:
		:
		: "memory", "cc");
}

变体 local_irq_save和local_irq_restore

local_irq_save会将arch_local_irq_save接口中读到的中断使能状态位返回,并关闭中断。此时如果返回的flg状态是关闭状态,那么就属于再次关闭。
local_irq_restore会将local_irq_save读到的flg状态传入arch_local_irq_restore,后者会将flg写回到CPSR中,恢复调用local_irq_save前的状态。

#define raw_local_irq_save(flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		flags = arch_local_irq_save();		\
	} while (0)
#define raw_local_irq_restore(flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		arch_local_irq_restore(flags);		\
	} while (0)

static inline unsigned long arch_local_irq_save(void)
{
	unsigned long flags;

	asm volatile(
		"	mrs	%0, cpsr	@ arch_local_irq_save\n"
		"	cpsid	i"
		: "=r" (flags) : : "memory", "cc");
	return flags;
}

/*
 * restore saved IRQ & FIQ state
 */
static inline void arch_local_irq_restore(unsigned long flags)
{
	asm volatile(
		"	msr	cpsr_c, %0	@ local_irq_restore"
		:
		: "r" (flags)
		: "memory", "cc");
}

设想这样一个情况:

  1. 本身CPSR中中断flg就是关闭的状态
  2. 调用了local_irq_disable
  3. 执行临界区中代码
  4. 调用了local_irq_enable

这个过程的结果就把本来的中断flg修改了。local_irq_save和local_irq_restore就是为了解决这种问题。

自旋锁

设计自旋锁的最初目的是在多处理器系统中提供对共享数据的保护,其背后的核心思想是设置一个在多处理器之间共享的全局变量锁 V,并定义当 V=1时为上锁状态,V=0为解锁状态。如果处理器 A 上的代码要进入临界区,它要先读取 V 的值,判断其是否为 0,如果V头0表明有其他处理器上的代码正在对共享数据进行访问,此时处理器 A 进入忙等待即自旋状态,如果 V=0 表明当前没有其他处理器上的代码进入临界区,此时处理器 A 可以访问该资源,它先把V置1(自旋锁的上锁状态),然后进入临界区,访问完毕离开临界区时将V置0(自旋锁的解锁状态)。
上述自旋锁的设计思想在用具体代码实现时的关键之处在于,必须确保处理器 A“读取V判断V的值与更新V”这一操作序列是个原子操作(atomic operation)。所谓原子操作,简单地说就是执行这个操作的指令序列在处理器上执行时等同于单条指令,也即该指令序列在执行时是不可分割的

spin_lock

spin_lock也是arch-specific的,不同的体系结构有不同的原子操作指令。

//精简过后的代码
typedef struct raw_spinlock {
	volatile unsigned int raw_lock;
} raw_spinlock_t;

typedef struct spinlock {
	union {
		strcut raw_spinlock rlock;
	};
} spinlock_t;

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

//raw_spin_lock是个宏,最后展开是这个样子(arm体系下)
static inline void raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable();		//如果定义了CONFIG_PREEMPT,内核支持可抢占的调度系统,将关闭调度器的可抢占特性,否则是空定义
	do_raw_spin_lock(lock); 
}

static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
	__acquire(lock);
	arch_spin_lock(&lock->raw_lock);
}

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned long tmp;

	__asm__ __volatile__(
"1:	ldrex	%0, [%1]\n"			//tmp = lock->raw_lock		L1
"	teq	%0, #0\n"				//测试tmp是否等于0			L2
	WFE("ne")					//如果tmp!=0 ,代表此时资源不空闲,执行WFE指令,让core进入low-power state
"	strexeq	%0, %2, [%1]\n"		//tmp==0,此时资源空闲,执行将1写入lock,将操作结果写入tmp	L3
"	teqeq	%0, #0\n"			//判断tmp是否为0,如果为0,则更新lock成功,此时lock=1,PC可以进入临界区。 L4
"	bne	1b"						//如果tmp不等于0,则跳到label 1		L5
	: "=&r" (tmp)
	: "r" (&lock->lock), "r" (1)
	: "cc");

	smp_mb();
}

preempt_disable();如果定义了CONFIG_PREEMPT,内核支持可抢占的调度系统,将关闭调度器的可抢占特性,否则是空定义。这里为什么要关闭调度器的可抢占特性:
防止因为调度器的可抢占性导致的竞态
在这里插入图片描述

spin_lock的变体

关闭中断,防止中断处理函数中的临界区与被中断的进程的临界区产生死锁。
spin_lock_irq
spin_lock_irqsave

在这里插入图片描述

引用

LDREX和STREX

从ARMv6架构开始,ARM处理器提供了Exclusive accesses同步原语,包含两条指令:

  • LDREX
  • STREX

LDREX和STREX指令,将对一个内存地址的原子操作拆分成两个步骤,

同处理器内置的记录exclusive accesses的exclusive monitors一起,完成对内存的原子操作。

LDREX

LDREX与LDR指令类似,完成将内存中的数据加载进寄存器的操作。

与LDR指令不同的是,该指令也会同时初始化exclusive monitor来记录对该地址的同步访问。例如

LDREX R1, [R0]
会将R0寄存器中内存地址的数据,加载进R1中并更新exclusive monitor。

STREX

该指令的格式为:

STREX Rd, Rm, [Rn]

STREX会根据exclusive monitor的指示决定是否将寄存器中的值写回内存中。

如果exclusive monitor许可这次写入,则STREX会将寄存器Rm的值写回Rn所存储的内存地址中,并将Rd寄存器设置为0表示操作成功。

如果exclusive monitor禁止这次写入,则STREX指令会将Rd寄存器的值设置为1表示操作失败并放弃这次写入。

应用程序可以根据Rd中的值来判断写回是否成功。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1、缓存击穿背后的问题

当面试官问:你知道什么是缓存击穿吗,你们是如何解决的? 首先我们要了解什么是缓存击穿?以及缓存击穿会引发什么问题? 缓存击穿就是redis中的热点数据过期,缓存失效,导致大量的请求直接打到数据…

【免费分享】数据可视化-银行动态实时大屏监管系统,含源码

一、动态效果展示 1. 动态实时更新数据效果图 ​ 2. 鼠标右键切换主题 二、确定需求方案 1. 屏幕分辨率 这个案例的分辨率是16:9,最常用的的宽屏比。 根据电脑分辨率屏幕自适应显示,F11全屏查看; 2. 部署方式 B/S方式:支持…

使用了不受支持的协议。 ERR_SSL_VERSION_OR_CIPHER_MISMATCH的问题解决办法

windwos 2008 R2 使用IIS部署的项目申请使用https协议的时候,通过安全加密协议访问网站提示不受支持的协议 错误原因分析 这种错误通常表示客户端和服务器之间存在协议版本或加密套件不兼容导致在SSL(Secure Socket Layer) 1.协议版本不兼容&…

壹[1],Xamarin开发环境配置

1,环境 VS2022 注: 1,本来计划使用AndroidStudio,但是也是一堆莫名的配置让人搞得很神伤,还是回归C#。 2,MAUI操作类似,但是很多错误解来解去,且调试起来很卡。 3,最…

哪个牌子的头戴式耳机好?推荐性价比高的头戴式耳机品牌

随着科技的不断发展,耳机市场也呈现出百花齐放的态势,从高端的奢侈品牌到亲民的平价品牌,各种款式、功能的耳机层出不穷,而头戴式耳机作为其中的一员,凭借其优秀的音质和降噪功能,受到了广大用户的喜爱&…

C++文件操作(2)

文件操作(2) 1.二进制模式读取文本文件2.使用二进制读写其他类型内容3.fstream类4.文件的随机存取文件指针的获取文件指针的移动 1.二进制模式读取文本文件 用二进制方式打开文本存储的文件时,也可以读取其中的内容,因为文本文件…

Flask 入门3:Flask 请求上下文与请求

1. 前言 Flask 在处理请求与响应的过程: 首先我们从浏览器发送一个请求到服务端,由 Flask 接收了这个请求以后,这个请求将会由路由系统接收。然后在路由系统中,还可以挂入一些 “勾子”,在进入我们的 viewFunction …

【C++】开源:Windows图形库EasyX配置与使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Windows图形库EasyX配置与使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下&#…

✅Redis 常见数据类型和应用场景(详解)

Redis 提供了丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合&…

揭开时间序列的神秘面纱:特征工程的力量

目录 写在开头1. 什么是特征工程?1.1 特征工程的定义和基本概念1.2 特征工程在传统机器学习中的应用1.3 时间序列领域中特征工程的独特挑战和需求3. 时间序列数据的特征工程技术2.1 数据清洗和预处理2.1.1 缺失值处理2.1.2 异常值检测与处理2.2 时间特征的提取2.2.1 时间戳解析…

循环——枚举算法2(c++)

目录 找和为K的两个元素 描述 在一个长度为n(n < 1000)的整数序列中&#xff0c;判断是否存在某两个元素之和为k。 输入 第一行输入序列的长度n和k&#xff0c;用空格分开。 第二行输入序列中的n个整数&#xff0c;用空格分开。 输出 如果存在某两个元素的和为k&…

个人建站前端篇(一)项目准备初始化以及远程仓库连接

云风的知识库 云风网前端重构&#xff0c;采用vue3.0vite antd框架&#xff0c;实现前后端分离&#xff0c;实现网站的SEO优化&#xff0c;实现网站的性能优化 vite创建vue项目以及前期准备 Vite 需要 Node.js 版本 18&#xff0c;20。然而&#xff0c;有些模板需要依赖更高…

STM32存储左右互搏 QSPI总线读写FLASH W25QXX

STM32存储左右互搏 QSPI总线读写FLASH W25QXX FLASH是常用的一种非易失存储单元&#xff0c;W25QXX系列Flash有不同容量的型号&#xff0c;如W25Q64的容量为64Mbit&#xff0c;也就是8MByte。这里介绍STM32CUBEIDE开发平台HAL库Qual SPI总线操作W25Q各型号FLASH的例程。 W25Q…

游泳耳机要怎么选购?一篇文章告诉你如何选购游泳耳机

在进行运动时享受音乐的乐趣是许多人的喜好&#xff0c;对于在地面展开的一般运动&#xff0c;选择耳机相对简单&#xff0c;但若是涉及水中游泳&#xff0c;我们就需要一款具备防水性能的专业游泳耳机。市面上已有数款针对游泳设计的防水耳机&#xff0c;本文将为您详细介绍如…

【解刊】审稿人极其友好!中科院2区SCI,3个月录用,论文质量要求宽松!

计算机类 • 高分快刊 今天带来Springer旗下计算机领域高分快刊&#xff0c;有投稿经验作者表示期刊审稿人非常友好&#xff0c;具体情况一起来看看下文解析。如有投稿意向可重点关注&#xff1a; 01 期刊简介 Complex & Intelligent Systems ✅出版社&#xff1a;Sprin…

光杆司令如何部署大模型?

1、背景 今天这种方式非常贴合低配置笔记本电脑的小伙伴们, 又没有GPU资源, 可以考虑使用api方式,让模型服务厂商提供计算资源 有了开放的api,让你没有显卡的电脑也能感受一下大模型管理知识库,进行垂直领域知识的检索和问答.算是自己初步玩一下AI agent 之前有写过一篇《平民…

Java二维码图片识别

前言 后端识别二维码图片 代码 引入依赖 <dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.2.1</version></dependency><dependency><groupId>com.google.zxing<…

软件压力测试:探究其目的与重要性

随着软件应用在各行各业中的广泛应用&#xff0c;确保软件在高负载和极端条件下的稳定性变得至关重要。软件压力测试是一种验证系统在不同负载条件下的性能和稳定性的方法。本文将介绍软件压力测试的目的以及为什么它对软件开发和部署过程至关重要。 验证系统性能的极限&#x…

二、人工智能之提示工程(Prompt Engineering)

黑8说 岁月如流水匆匆过&#xff0c;哭一哭笑一笑不用说。 黑8自那次和主任谈话后&#xff0c;对这个“妖怪”继续研究&#xff0c;开始学习OpenAI API&#xff01;关注到了提示工程(Prompt Engineering)的重要性&#xff0c;它包括明确的角色定义、自然语言理解&#xff08;…

10个关键字让你的谷歌竞价排名瞬间飙升-华媒舍

在现代社会中&#xff0c;搜索引擎已经成为获取信息的主要途径之一。在这其中&#xff0c;谷歌搜索引擎以其强大的搜索算法和智能化的用户体验而闻名。对于企业主来说&#xff0c;如何提高在谷歌搜索结果中的排名&#xff0c;对于他们的品牌推广和获取潜在客户非常重要。 1. 关…