同步 -- 自旋锁

news2024/11/24 14:53:00

基础学习--原子操作

typedef struct {
	int counter;
} atomic_t;


static __always_inline void
atomic_set(atomic_t *v, int i)
{
	instrument_atomic_write(v, sizeof(*v));
	raw_atomic_set(v, i);
}


static __always_inline void
raw_atomic_set(atomic_t *v, int i)
{
	arch_atomic_set(v, i);
}

#define arch_atomic_read(v)			__READ_ONCE((v)->counter)
#define arch_atomic_set(v, i)			__WRITE_ONCE(((v)->counter), (i))

#define __WRITE_ONCE(x, val)						
do {									
	*(volatile typeof(x) *)&(x) = (val);  //typeof返回变量的类型				 
} while (0)

结构体

spinlock -> raw_spinlock -> arch_spinlock_t(qspinlock)

typedef struct spinlock {
	union {
		struct raw_spinlock rlock; //自旋锁的核心成员是raw_spinlock锁
	};
} spinlock_t;


typedef struct raw_spinlock {
	arch_spinlock_t raw_lock; //raw_lock的核心成员是arch_spinlock_t,它与具体架构有关
} raw_spinlock_t;


//ARM64位架构中,为qspinlock
typedef struct qspinlock {
	union {
		atomic_t val; //原子变量
#ifdef __LITTLE_ENDIAN
		struct {
			u8	locked;  //最优先持锁标志,即当unlock之后,这个位被置位的CPU最先持锁,1和0
			u8	pending; //表示这个锁是否被人持有,1被人持有,0无人持锁
		};
		struct {
			u16	locked_pending; //由locked 和 pending构成
			u16	tail; //由idx CPU构成,用来标识等待队列最后一个节点
		};
} arch_spinlock_t;

相关API的实现

调用逻辑:

spin_lock_* -> raw_spin_lock_* -> _raw_* -> arch_spin_lock(架构相关,qspinlock)

spin_lock_init

自旋锁的初始化:就是做个spinlock->raw_spinlock->arch_spinlock_t的转换,然后把arch_spinlock_t的val初始化为0

include\linux\spinlock_types_up.h

# define spin_lock_init(_lock)			
do {						
	spinlock_check(_lock);			
	*(_lock) = __SPIN_LOCK_UNLOCKED(_lock);	
} while (0)

spin_lock

加锁:直接加锁,如果成功则返回,失败,则进入qspinlock的具体实现方式上

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

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable();//关抢占
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
}
#define spin_lock_irqsave(lock, flags)				\
do {								\
	raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
} while (0)

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	unsigned long flags;
 
	local_irq_save(flags);//关本地中断,并保存中断状态
	preempt_disable();//关抢占
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	return flags;
}


#define arch_spin_lock(l)        queued_spin_lock(l)
#define arch_spin_trylock(l)        queued_spin_trylock(l)
 
#ifndef queued_spin_lock

static __always_inline void queued_spin_lock(struct qspinlock *lock)
{
	int val = 0;
	
	if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
		return;
 
	queued_spin_lock_slowpath(lock, val);//这里就是直接获取锁失败的情况,需要自旋等待了。
}

qspinlock

qspinlock的实现是建立在MCS锁的理论基础上。

struct mcs_spinlock {
        struct mcs_spinlock *next;
        int locked;
};

mcs_spinlock中next成员就是构建单链表的基础,spin等锁的操作只需要将所属自己CPU的mcs_spinlock结构体加入单链表尾部,然后spin,直到自己的mcs_spinlock的locked成员置1(locked初始值是0)。unlock的操作也很简单,只需要将解锁的CPU对应的mcs_spinlock结构体的next域的lock成员置1,相当于通知下一个CPU退出循环。

以4个CPU的系统为例说明。首先CPU0申请spinlock时,发现链表是空,并且锁是释放状态。所以CPU0获得锁。

CPU1继续申请spinlock,需要spin等待。所以将CPU1对应的mcs_spinlock结构体加入单链表尾部。然后spin等待CPU1对应的mcs_spinlock结构体locked成员被置1。

当CPU2继续申请锁时,发现链表不为空,说明有CPU在等待锁。所以也将CPU2对应的mcs_spinlock结构体加入链表尾部。

当CPU0释放锁的时候,发现CPU0对应的mcs_spinlock结构体的next域不为NULL,说明有等待的CPU。然后将next域指向的mcs_spinlock结构体的locked成员置1,通知下个获得锁的CPU退出自旋。MCS lock头指针可以选择不更新,等到CPU2释放锁时更新为NULL。

通过以上步骤,我们可以看到每个CPU都spin在自己的使用变量上面。

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

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

相关文章

集丰照明|别墅设计:从空间规划到奢华元素的精心打造

当人们谈论别墅时,总会不自觉地与豪华、舒适、私人空间等词汇联系在一起。确实,别墅作为一种住宅形式,其设计不仅需要满足基本的生活需求,更要有独特的风格和品味。本文将从别墅设计的各个方面进行探讨,带您领略从空间…

uni-app:通过三目运算动态增加样式效果(class)

效果 代码 第一条:当变量line的值等于abc时,class就等于yes,反之class等于no(显然等于abc,执行yes,前景色为红色) 第一条:当变量line1的值等于abc时,class就等于yes,反之class等于no&#xff…

【实验】H3C校园双出口配置案例,可跟做!

【微|信|公|众|号:厦门微思网络】 1.案例拓补 该拓扑图中的校园网内部分为两个网段:一个为学生校舍网段(192.168.2.0),主要访问电信提供的internet服务器;另外一个网段为校园办公和教学用网段(…

python 第一次作业

1.使用turtle换一个五环 2.设计这样一个程序:输入一个数字 判断它是不是一个质数 使用turtle换一个五环: >>> import turtle #导入模块 >>> turtle.width(10) #设置圆圈宽度 >>> turtle.color("blue&qu…

DC电源模块具有不同的安装方式和安全规范

BOSHIDA DC电源模块具有不同的安装方式和安全规范 DC电源模块是将低压直流电转换为需要的输出电压的装置。它们广泛应用于各种领域和行业,如通信、医疗、工业、家用电器等。安装DC电源模块应严格按照相关的安全规范进行,以确保其正常运行和安全使用。 D…

快速上手Linux基础开发工具

目录 软件包管理器 概念理解 用法示例 - 以yum为例 vim 模式的切换 常用操作 插件和配置 gcc/g gdb make / makefile 软件包管理器 概念理解 在Linux下安装软件的话,一个比较原始的办法是下载程序的源代码,然后进行编译,进而得到…

vscode 无法使用 compilerPath“D:.../bin/arm-none-eabi-g++.exe”解析配置。

最近在使用vscode搭建ODrive STM32开发环境,依次安装了以下内容: 1.Python3: 用于运行工程构建脚本 2.ST-Link/V2 Drivers: STLink/v2编程器的驱动 3.Visual Studio Code: 轻量级但功能强大的源代码编辑器 …

Python的简单使用与应用

在当今互联网时代,网络爬虫成为了获取数据的重要工具之一。而使用代理IP进行爬虫操作,则是提高爬虫效率、绕过访问限制的利器。本文将向大家介绍Python代理IP爬虫的简单使用,帮助大家了解代理IP的原理、获取代理IP的方法,并探索其…

花了一周时间,更新了下软考云题库Web版

花了一周时间,更新了下软考云题库Web版,体验地址和体验账户在文章最后。想体验的直接翻到最后。 软考云题库的Web版的由来 之前,有朋友提出在小程序中刷题不太方便,希望能在电脑上进行题目练习。于是,我们着手开发了…

AH8316芯片:高效12V转5V 5A电源解决方案

AH8316是一款内置MOS的高效电源转换芯片,具有广泛的应用领域。本文将介绍AH8316芯片的主要特性和性能指标,以及其在12V转5V 5A电源转换中的应用。 AH8316芯片主要特性: 1. 输入电压范围广泛:芯片支持7V至32V的输入电压范围&#…

阿里云无影云电脑角色AliyunServiceRoleForGws什么意思?

阿里云无影云电脑服务关联角色是指角色名称:AliyunServiceRoleForGws,并赋予角色权限策略:AliyunServiceRolePolicyForGws的过程,简单来说,就是允许无影云电脑服务访问您VPC、CEN和NAS中的资源,使用该权限查…

无涯教程-JavaScript - MINVERSE函数

描述 MINVERSE函数返回存储在数组中的矩阵的逆矩阵。 语法 MINVERSE (array)争论 Argument描述Required/OptionalArrayA numeric array with an equal number of rows and columns.Required Notes 数组可以作为单元格范围(如A1:C3)或数组常数(如{1,2,3; 4,5,6; 7,8,9})或这…

Java增强for循环(学习笔记)

Java增强for循环 主要用于数组或者集合的增强型for循环。 格式: for(声明语句:表达式){ 代码句子 } 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元…

Paragon NTFS For Mac2023破解版免费下载安装激活

Paragon NTFS For Mac 2023破解版是一款强大的ntfs磁盘读写工具,帮助用户在MAC电脑上读写NTFS格式的磁盘,需要解决MAC无法读写磁盘的用户可以来试试Paragon磁盘读写软件哦。 Paragon NTFS 2023 专门为解决mac读写开发的软件,它为中国用户量体裁衣,提供Ma…

iText实战--PDF和iText 简介

PDF可以做如下事务 iText 5步创建PDF import java.io.FileOutputStream; import com.itextpdf.text.Document; import com.itextpdf.text.Paragraph; import com.itextpdf.text.pdf.PdfWriter;public class PdfHelloWorld {/*** param args*/public static void main(String[]…

动态内存管理(malloc、free、calloc、realloc函数简介)

动态内存管理 🎃为什么存在动态内存管理🎃动态内存函数的介绍🎊malloc🎋perror函数 🎊free🎊calloc🎊realloc 🎃为什么存在动态内存管理 在此之前,我们开辟内存空间有两种…

shell脚本执行中报错: line 10: 09: value too great for base (error token is “09“)

shell脚本执行中报错: line 10: 09: value too great for base (error token is “09”) 这个错误通常是由于 shell 脚本中对一个数值使用了以零开头的八进制表示方式,而八进制表示中的数字 8 和 9 是无效的引起的。shell 默认将以零开头的数字解释为八进…

【网络安全】你必须知道的几个网络安全概念

一、安全 Web 网关 安全 Web 网关已经从其过去优化互联网带宽的目的演变为保护用户免受来自互联网的恶意内容的侵害。诸如 URL 过滤、反恶意软件、解密和检查通过 HTTPS 访问的网站、数据丢失防护 (DLP) 和云访问安全代理 (CASB) 等功能现已成为标准。 二、 DDoS 防御 DDoS …

在仿真和代码执行期间在参数值集之间切换

目录 浏览模型示例 将参数值存储在结构体数组中 创建变量以在参数集之间切换 使用总线对象作为结构体数组的数据类型 使用枚举类型切换变量 向数据字典添加新对象 仿真期间在参数集之间切换 生成和检查代码 要存储相同模块参数的多个独立值集,可以使用结构体…

局部变量,全局变量与内存

本文会使用IDA分析局部变量&#xff0c;全局变量在内存的存储 目录 使用IDA分析局部变量 使用IDA分析全局变量 总结 使用IDA分析局部变量 #include <stdio.h>int main() {int nNum 1;float fNum 2.5;char ch A;printf("int %d, float %f, char %c", nNu…