Linux经典自旋锁

文章详细介绍了AArch64架构下自旋锁的原理和实现,包括LL/SC原子指令的使用,以及如何处理大小端模式。代码中展示了如何通过汇编指令进行锁的获取和释放,特别地,通过ROR和LSR指令处理锁的状态检查。
摘要由CSDN通过智能技术生成

经典自旋锁原理

待补充

数据结构

arch/arm64/include/asm/spinlock_types.h

typedef struct {
#ifdef __AARCH64EB__ 	/* 大端字节序(高位存放在低地址) */
	u16 next;
	u16 owner;
#else  					/* 小端字节序(低位存放在低地址) */
	u16 owner;			//低地址
	u16 next;			//高地址
#endif
} __aligned(4) arch_spinlock_t;

源码实现

spin_lock

//lockval			=> %0
//newval			=> %1
//tmp				=> %2
//*lock				=> %3
//lock->owner		=> %4
//1 << 16			=> %5
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned int tmp;
	arch_spinlock_t lockval, newval;

	asm volatile(
	/* 自动实现下一次排队 */
	ARM64_LSE_ATOMIC_INSN(
	/* LL/SC */
"	prfm	pstl1strm, %3\n"
"1:	ldaxr	%w0, %3\n" 				//把*lock加载到变量lockval中, lockval就是代表自己的锁(w代表写入长度,b=8;w=16;l=32)
"	add	%w1, %w0, %w5\n"			//newval = lockval + 1 << TICKET_SHIFT =等价=> newval.next = lockval.next + 1
"	stxr	%w2, %w1, %3\n"			//尝试独占写,*lock = newval
"	cbnz	%w2, 1b\n",				//如果写*lock失败则跳转到1,意味着有别人抢先排队给*lock.next+1了
	/* LSE atomics */
"	mov	%w2, %w5\n"
"	ldadda	%w2, %w0, %3\n"
"	nop\n"
"	nop\n"
"	nop\n"
	)

	/* 是否获得了锁? */
"	eor	%w1, %w0, %w0, ror #16\n"	//lockval右移16位获得owner域与自己比较,即比较lockval的owner和next成员
"	cbz	%w1, 3f\n"					//如果是0,直接持锁,跳转3,进入临界区
	/*
	 * No: spin on the owner. Send a local event to avoid missing an
	 * unlock before the exclusive load.
	 */
	/* 若没有获得锁则自旋 */
	/* 发送本地事件,避免在独占加载前忘记解锁 */
"	sevl\n"							//不理解
"2:	wfe\n"							//WFE指令=>wait for event,进入睡眠模式(降低功耗)
"	ldaxrh	%w2, %4\n"				//tmp = lock->owner(当前锁的owner)(ldaxrh中h代表halfword,即2字节,a不懂)
"	eor	%w1, %w2, %w0, lsr #16\n"	//lockval.owner == lock->owner?相等则w1=0
"	cbnz	%w1, 2b\n"				//若w1非0,则跳转回2
	/* We got the lock. Critical section starts here. */
"3:"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock) //lockval=>%0 ... *lock=>%3
	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT) // %4 %5
	: "memory");
}

补充:汇编指令

1、MOV
在寄存器之间移动数据,或者向寄存器中写立即数

MOV     R1,R2    ;//R1=R2

2、LDR和STR
LDR是将内存的数据加载到寄存器中,或是载入立即数

LDR     R1,=0xE0000000  ;//R1=0xE0000000
LDR     R1,0xE0000000   ;//将内存中地址为0xE0000000的内容载入到R1
LDR     R1,[R0]         ;//将R0中的数所指定的地址的内容传输到R1

STR是将寄存器中的数字载入内存。格式如下:STR{条件} 源寄存器,<存储器地址>

STR     R1,[R0]     ;//将R1中的内容传输到R0中的数所指定的地址的内存中去

3、STXR
store exclusive

Syntax: STXR Ws, <Wt/Xt>, [Xn]
Ws indicates whether the store completed successfully(0=success)

4、CBZ和CBNZ

CBZ Rn, label  //Rn为0则跳转到label
CBNZ Rn, label //Rb非0则跳转到label
//举例
1:xxx
  yyy
cbnz	%w2, 1b

5、EOR
逻辑异或操作指令

EOR{<cond>}{s} <Rd>, <Rn>, <shifter_operand>
<Rd>目标寄存器,也就是存结果的寄存器
<Rn>寄存器为第一个源操作数所在的寄存器
<shifter_operand>寄存器为第二个操作数

6、ROL和ROR

ROL循环左移,ROR循环右移
以ROR为例:ROR可完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,**左端用右端移出的位来填充**
ROR的格式为:通用寄存器,ROR 操作数

7、LSL和LSR

LSL左移,ROR右移
LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充
LSR的格式为:
通用寄存器,LSR 操作数

问题

1、为什么同时用了ROR和LSR,貌似用一个就够了
https://zhuanlan.zhihu.com/p/83578174

2、大小端模式不同,为什么TICKET_SHIFT能写死为16

3、"eor %w1, %w2, %w0, lsr #16"为什么能去除lockval的next域

4、“发送本地事件,避免在独占加载前忘记解锁”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值