前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spin_lock的变体

spin_lock的变体

作者头像
DragonKingZhu
发布2020-03-24 10:54:17
8080
发布2020-03-24 10:54:17
举报
文章被收录于专栏:Linux内核深入分析

spin_lock变体的引入

考虑如下图所示的情况:

当处理器上当前进程A需要对共享变量a操作,所以在操作前通过spin_lock获取锁进入临界区,如上图标号1。当进程A进入临界区后,进程A所在的处理器发生了一个外部硬件中断,此时系统必须停下进程A的执行转向执行中断,如上图标号2。假设中断处理程序也需要操作共享变量a,所以在操作之前也许要调用spin_lock获取锁来操作变量a。当中断处理程序试图去获取变量a的时候,因为之前被中断的进程A已经获取了锁,于是将导致中断处理程序进入自旋状态。在中断处理程序中出现自旋是非常致命的,因为中断处理程序必须尽可能短的返回。同时被中断进程A因中断处理程序不能返回而无法恢复执行,也就不可能释放锁,所以将导致中断处理程序一直自旋下去,出现死锁。所以就引入了spin_lock的变体出现。

spin_lock_irq

spin_lock_irq对比之前的spin_lock的不同是: 在进入临界区的时候增加关闭本地处理器响应中断的能力。

代码语言:javascript
复制
static inline void spin_lock_irq(spinlock_t *lock)
{
	raw_spin_lock_irq(&lock->rlock);
}

#define raw_spin_lock_irq(lock)		_raw_spin_lock_irq(lock)

void __lockfunc _raw_spin_lock_irq(raw_spinlock_t *lock)
{
	__raw_spin_lock_irq(lock);
}

static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
	local_irq_disable();
	preempt_disable();
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

根据上面代码可以明显的看见spin_lock_irq对比spin_lock就是多了local_irq_disable,关于别的内容不再解释,可以详看spin_lock一节

local_irq_disable

local_irq_disable是用来关闭本地处理器的中断。

代码语言:javascript
复制
#define local_irq_disable()	do { raw_local_irq_disable(); } while (0)
#define raw_local_irq_disable()		arch_local_irq_disable()

因为arch_local_irq_disable的实现是和具体的硬件平台有关,所以以ARM平台为主分析其实现。

local_irq_disable在ARM32上实现

代码语言:javascript
复制
/*
 * Disable IRQs
 */
static inline void arch_local_irq_disable(void)
{
	unsigned long temp;
	asm volatile(
		"	mrs	%0, cpsr	@ arch_local_irq_disable\n"
		"	orr	%0, %0, #128\n"
		"	msr	cpsr_c, %0"
		: "=r" (temp)
		:
		: "memory", "cc");
}

如果还有朋友不熟悉嵌入汇编的话,可以阅读前面<GCC内嵌汇编>小节。

在分析代码之前,需要解释下CPSR寄存器以及格式。

cpsr寄存器

程序状态寄存器(current program status register) (当前程序状态寄存器),在任何处理器模式下被访问。它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。

而操作cpst寄存器是不能使用mov,ldr等通用的指令,只能使用特权指令: MSR, MRS

MRS R1,CPSR ; 将CPSR状态寄存器读取,保存到R1中 MSR SPSR, R2 ; 将R2的值保存到SPSR状态寄存器中

这里不把每一位都介绍到位,只介绍需要的而已。

bit[6] : 快速中断屏蔽位,置为1表示禁止快速中断,置为0表示打开快速中断

bit[7] : 外部中断屏蔽位,置为1表示禁止外部中断,置为0表示打开外部中断

local_irq_disable代码分析

汇编

c语言

解释

mrs %0, cpsr

temp=cpsr

将cpsr的值赋值为temp临时变量

orr %0, %0, #128

bit[7]=1

将Bit7置1,将结果保存到temp中

msr cpsr_c, %0

cpsr=temp

将temp中的值赋值为cpsr

通过上述的操作,就可以将cpsr的bit设置为1,从而关闭了本地cpu中断。

local_irq_disable在ARM64上实现

代码语言:javascript
复制
static inline void arch_local_irq_disable(void)
{
	asm volatile(
		"<span style="font-family: Arial, Helvetica, sans-serif;">msr	daifset, #2  </span>// arch_local_irq_disable"
		:
		:
		: "memory");
}

可以看到上述的代码非常少,关键之处还是在daifset指令,查看armV8参考手册。

上面的意思是在ARM64中可以使用DAIFSet, DAIFClr可以直接的操作PSTATE区域。

MSR DAIFSet, #Imm4 ; 用于设置DAIF域为1

MSR DAIFClr, #Imm4 ; 用与清除DAIF域为0

而其中的DAIF代码的是:

代码语言:javascript
复制
type ProcState is (
....
bits (1) D, // Debug mask bit                       [AArch64 only]
bits (1) A, // Asynchronous abort mask bit
bits (1) I, // IRQ mask bit
bits (1) F, // FIQ mask bit
.....
)

而根据代码:

代码语言:javascript
复制
msr	daifset, #2	

可以知道,#Imm4=2的时候的含义是:

红色区域的意思就是设置DAIF中的I位为1, 设置为1就代码关闭了IRQ中断。

至于arch_local_irq_enable函数原理相通,在这里不做过多解释。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • spin_lock变体的引入
  • spin_lock_irq
  • local_irq_disable
    • local_irq_disable在ARM32上实现
      • cpsr寄存器
    • local_irq_disable代码分析
      • local_irq_disable在ARM64上实现
      相关产品与服务
      腾讯云代码分析
      腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,助力维护团队卓越代码文化。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档