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			L2WFE("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.mzph.cn/news/665022.shtml

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

相关文章

金和OA jc6 UploadFileBlock 任意文件上传漏洞复现

0x01 产品简介 金和OA协同办公管理系统软件(简称金和OA),本着简单、适用、高效的原则,贴合企事业单位的实际需求,实行通用化、标准化、智能化、人性化的产品设计,充分体现企事业单位规范管理、提高办公效率的核心思想,为用户提供一整套标准的办公自动化解决方案,以帮助…

JavaWeb01-JDBC、Druid连接池

目录 一、JDBC 1.概述 2.本质 3.好处 4.使用步骤 5.JDBC_API (1)DriverManager(驱动管理类) (2)Connection(数据库连接对象) (3)Statement &#xf…

博客摘录「 MAC 安装electron 报权限错误 npm权限错误」

1.mac下安装electron 在运行 npm install electron 时,有些用户会偶尔遇到安装问题。在大多数情况下,这些错误都是由网络问题导致,而不是因为 electron npm 包的问题。 如 ELIFECYCLE、EAI_AGAIN、ECONNRESET 和 ETIMEDOUT 等错误都是此类网络…

基于Java SSM框架实现汉服文化平台系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现汉服文化平台系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个汉服文化平台网站 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论…

Linux中make和makefile

make与makefile 简单介绍常见用法符号替代自动寻找设置变量取消打印提示 简单介绍 make是Linux中一个命令,与之匹配的是makefile,makefile是一个文件。make会根据makefile中的内容完成对应的工作 创建一个名为makefile的文件 vim打开makefile 第一行是依…

Redis常用数据类型--List

List 常用命令LPUSH/RPUSHLPUSHX/RPUSHXLRANGELPOP/RPOPLINDEXLINSERTLLENBLPOP/BRPOP 内部编码典型应用场景 常用命令 LPUSH/RPUSH 将⼀个或者多个元素从左侧(头插) / 右侧(尾插)放入到 list 中 LPUSH key element [element ...]/RPUSH key element [element ...]时间复杂度…

lwip通过结构体解析数据,通讯原始数据与结构体

涉及数据格式 下面一段代码在ip4.h PACK_STRUCT_BEGIN /* The IPv4 header */ struct ip_hdr {/* version / header length */PACK_STRUCT_FLD_8(u8_t _v_hl);/* type of service */PACK_STRUCT_FLD_8(u8_t _tos);/* total length */PACK_STRUCT_FIELD(u16_t _len);/* identi…

el-table @row-click影响复制以及el-table-column的点击事件

问题一影响复制 在row-click的函数里面加一个内容判断 if (window.getSelection().toString() ) 只有当选中的内容为空的时候才执行row-click 问题二影响el-table-column的点击事件 el-table-column的点击事件上的点击事件加上.stop click.stop修饰符可以阻止事件继续向上传…

调试以及发布npm组件

开发原因: 由于公司自己的封装到npm的组件有点问题,负责人由在忙其他,就由我去负责改改,中途出了不少问题,记录一下。 一、下载源码 第一步肯定是去git上把组件的源码下载下来,这一步没什么好说&#xf…

好的问卷设计标准:确保数据质量与准确性的关键要素

问卷的主要由三个部分组成:问卷说明、问卷主题、问卷结束。而这三个部分又包含了很多因素,比如问卷主题、问卷标题、问卷题目、问卷调查对象等。制作问卷不仅仅是简单的问题罗列,然后进行发放。不同质量的调查问卷会反馈出不一样的效果&#…

Vue.js设计与实现(霍春阳)

Vue.js设计与实现 (霍春阳) 电子版获取链接:Vue.js设计与实现(霍春阳) 编辑推荐 适读人群 :1.对Vue.js 2/3具有上手经验,且希望进一步理解Vue.js框架设计原理的开发人员; 2.没有使用过Vue.js,但对Vue.js框架设计感兴趣…

2024年美赛E题:财产保险的可持续性 Sustainability of Property Insurance 思路模型代码解析

2024年美赛E题:财产保险的可持续性 Sustainability of Property Insurance 思路模型代码解析 【点击最下方群名片,加入群聊,获取更多思路与代码哦~】 问题翻译 极端天气事件对房产所有者和保险公司已经成为一场危机。近年来,世界…

RabbitMQ-中死信交换机

在RabbitMQ中,死信交换机(DLX,Dead Letter Exchange)是一种用于处理无法正常消费的消息的机制。当消息在一个队列中变成死信(dead letter)之后,它可以被重新发布到另一个交换机,这个…

Blender教程(基础)-面的切割-10

快捷键K切割,菜单选项切割. 一、随意切割 物体在编辑模式下,按键盘K建切割物体。 二、中点切割 先按K键,再按shift键,会自动吸附到每条边的中点进行切割。 三、取消吸附 切割时会自动吸附到顶点或边 关闭快速吸附 按K键再按…

26条prompt规则应用于大模型

1、引入动机 llm大模型在回答一些问题上表现出了惊人的能力,例如数学逻辑推理,代码生成,问题答复等。提词工程是和大预言模型交流的一门艺术。 大模型的返回结合和用户的指令和输入直接相关prompts是用户和大模型沟通的一种编码方式 一般地…

在低代码平台上实现精益软件开发:提高效率与灵活性的关键实践

什么是精益软件开发? 精益软件开发是一种敏捷的软件开发框架。它基于最小化浪费和最大化价值的原则。该框架基于最小可行产品策略运行,该策略强调交付具有基本基本功能的产品,然后根据收到的反馈进行迭代以即兴发挥并提供卓越。 精益软件开发…

2阶段提交_3阶段提交(phase-commit)

1. 2PC(两阶段提交) 如上所示是2阶段提交的一个过程,可为什么要进行两阶段提交呢?这里主要来说是将操作事务能力和提交、回滚事务能力分开来做成2阶段,如果不分开会造成什么后果呢: 如果单纯 A 向 B 发送一个请求就以…

由于找不到d3dcompiler43.dll无法继续执行程序的解决方法

在日常使用电脑的过程中,我们常常会遭遇一些突发的技术问题,其中之一便是可能会遇到系统提示找不到d3dcompiler43.dll文件的情况。这一特定的动态链接库文件(dll)对于许多应用程序的正常运行至关重要,尤其是在涉及到图…

什么是Golang中的包循环问题?如何规避?

Go语言作为一门注重简洁和效率的编程语言,在设计上有着严格的规范和原则。其中一个突出的设计决策是禁止包循环。接下来将深入解释为何Go语言中不允许包循环,如何避免它,以及如何进行好的规划,通过实际开发案例和代码演示&#xf…

PPT母版页码设置

PPT母版页码设置 一、需求介绍二、达到效果三、具体操作1. 插入页码2. 设置起始页码为03. 进入母版编辑页面4. 内容格式调整5. 删去最后一个板式的三个模块信息6. 尾页处理7. 最终效果 一、需求介绍 PPT的母版可以设定PPT的基调,且在非母版页面不会误改PPT中的固定…