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

news2024/10/6 18:24:11

互斥与同步

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

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

本节首要目标:

  • 考察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.coloradmin.cn/o/1428164.html

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

相关文章

年轻人“过年”行为大赏

【潮汐商业评论/原创】 前脚刚进家门,后脚快递电话一个接着一个。 临近春节,Julia是提前批回家的年轻人,与Julia一同到家的还有她的年货。上至大小家电,下到坚果零食,短短几天快递就堆满了客厅。 吃的喝的还能理解&…

Tencent Tinker:移动应用热修复的未来之路

Tencent Tinker:移动应用热修复的未来之路 1 引言 移动应用热修复是一项在移动应用开发领域中日益重要的技术,它可以帮助应用程序开发者快速修复线上应用的bug、漏洞和功能问题,而无需重新发布整个应用。这种能力对于提高用户体验、降低用户…

卸载Ubuntu双系统

卸载Ubuntu双系统 我们卸载Ubuntu双系统,可能出于以下原因: 1、Ubuntu系统内核损坏无法正常进入 2、Ubuntu系统分配空间不足,直接扩区较为复杂 3、以后不再使用Ubuntu,清理留出空间 123无论出于哪种原因,我们都是要…

vue项目在public中编写一个json文件 并用http请求获取 模仿数据接口

我们前后端分离是经常存在 前端已经开发到需要接口 但后端还没开始的情况的 如果直接在js中写假数据 后面还要改不少东西 多少有点麻烦 我们可以 直接在 public 静态资源目录下创建一个json文件 这里 我取名叫 city.json 大家可以根据自己喜好给json文件命名 我这个json文件的…

LEETCODE 170. 交易逆序对的总数

class Solution { public:int reversePairs(vector<int>& record) {if(record.size()<1)return 0;//归并 递归int left,right;left0;rightrecord.size()-1;int nummergeSort(left,right,record);return num;}int mergeSort(int left,int right, vector<int>…

WordPress可以做企业官网吗?如何用wordpress建公司网站?

我们在国内看到很多个人博客网站都是使用WordPress搭建&#xff0c;但是企业官网的相对少一些&#xff0c;那么WordPress可以做企业官网吗&#xff1f;如何用wordpress建公司网站呢&#xff1f;下面boke112百科就跟大家简单说一下。 WordPress是一款免费开源的内容管理系统&am…

前端JavaScript篇之对对象与数组的解构的理解、如何提取高度嵌套的对象里的指定属性?

目录 对对象与数组的解构的理解如何提取高度嵌套的对象里的指定属性&#xff1f; 对对象与数组的解构的理解 对象与数组的解构是一种通过模式匹配的方式&#xff0c;从对象或数组中提取值&#xff0c;并将其赋给变量的过程。它可以让我们以一种简洁的方式访问和使用对象或数组…

flask基于大数据的旅游景区推荐可视化大屏系统 juj13-vue

本论文分为六个章节。 第一章&#xff0c;绪论&#xff0c;其包含课题背景及意义&#xff0c;现国内外的发展现状&#xff0c;本课题要研究的内容&#xff0c;所使用开发工具的描述等信息。 第二章&#xff0c;主要介绍了系统的开发技术。 第三章&#xff0c;先讲述功能需求分析…

留学生怎么合理使用ChatGPT ?还有哪些同类工具可以使用?

一篇篇相关于ChatGPT的文章陆陆续续铺天盖地的出现在我们面前。今天我们来看看怎么使用这个宝藏工具&#xff01; 文章主要内容为&#xff1a; 1.它是什么&#xff1f; 2.它能做什么&#xff1f; 3.作为留学生我们怎么使用它&#xff1f; 4.其他同类工具推荐 5.个人观点 一…

第 8 章:Linux中使用时钟、计时器和信号

在本章中&#xff0c;我们将开始探索Linux环境中可用的各种计时器。随后&#xff0c;我们将深入了解时钟的重要性&#xff0c;并探讨UNIX时间的概念。接下来&#xff0c;我们将揭示在Linux中使用POSIX准确测量时间间隔的方法。之后&#xff0c;我们将进入std::chrono的领域&…

市场复盘总结 20240202

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 昨日主题投资 连板进级率 6/30 20% 二进三…

vue全家桶之路由管理Vue-Router

一、前端路由的发展历程 1.认识前端路由 路由其实是网络工程中的一个术语&#xff1a; 在架构一个网络时&#xff0c;非常重要的两个设备就是路由器和交换机。当然&#xff0c;目前在我们生活中路由器也是越来越被大家所熟知&#xff0c;因为我们生活中都会用到路由器&#…

基于最新koa的Node.js后端API架构与MVC模式

Koa 是一个由 Express 原班人马打造的现代 Web 框架&#xff0c;用于 Node.js。它旨在提供一个更小、更富有表现力和更强大的基础&#xff0c;用于 Web 应用和 API 开发。Koa 不捆绑任何中间件&#xff0c;它提供了一个优雅的方法以组合不同的中间件来处理请求和响应。 Koa 的核…

【QT】坐标系统和坐标变换

目录 1 坐标变换函数 1.1 坐标平移 1.2 坐标旋转 1.3 缩放 1.4 状态保存与恢复 2 坐标变换绘图实例 2.1 绘制3个五角星的程序 2.2 绘制五角星的PainterPath的定义 3 视口和窗口 3.1 视口和窗口的定义与原理 3.2 视口和窗口的使用实例 4 绘图叠加的效果 1 坐标变换函数 QPainter…

链表与二叉树-数据结构

链表与二叉树-数据结构 创建叶子node节点建立二叉树三元组&#xff1a;只考虑稀疏矩阵中非0的元素&#xff0c;并且存储到一个类&#xff08;三元组&#xff09;的数组中。 创建叶子node节点 class Node{int no;Node next;public Node(int no){this.nono;} } public class Lb…

24.云原生ArgoCD高级之数据加密seale sealed

云原生专栏大纲 文章目录 数据加密之seale sealedBitnami Sealed Secrets介绍Bitnami Sealed Secrets工作流程安装sealed-secrets和kubeseal安装sealed-secrets-controller安装kubeseal通过kubeseal将sealed-secrets公钥拿出来通过kubeseal加密secrets替换kustomize下secret为…

Leetcode 热门百题斩(第二天)

介绍 针对leetcode的热门一百题&#xff0c;解决大多数实习生面试的基本算法题。通过我自己的思路和多种方法&#xff0c;供大家参考。 1.两数之和&#xff08;题号&#xff1a;1) 方法一 最先想到的就是两个for去遍历匹配。 class Solution {public int[] twoSum(int[]…

DPVS 多活部署架构部署

一、目标 利用DPVS部署一个基于OSPF/ECMP的提供HTTP服务的多活高可用的测试环境。 本次部署仅用于验证功能&#xff0c;不提供性能验证。 配置两台DPVS组成集群、两台REAL SERVER提供实际HTTP服务。 注&#xff1a;在虚拟环境里面&#xff0c;通过在一台虚拟服务器上面安装FR…

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…

阿里云如何找回域名,进行添加或删除?

权威域名管理介绍说明&#xff0c;包含添加域名、删除域名、找回域名、域名分组等操作介绍。 一、添加域名 非阿里云注册域名或子域名如需使用云解析DNS&#xff0c;需要通过添加域名功能&#xff0c;将主域名或子域名添加到云解析控制台&#xff0c;才可以启用域名解析服务。…