02-Linux Kernel(armv8-aarch64)的原子操作的底层实现

快速链接:
.
👉👉👉 个人博客笔记导读目录(全部) 👈👈👈


说明:
在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14


通常我们代码中的a = a + 1这样的一行语句,翻译成汇编后蕴含着3条指令:

ldr x0, &a
add x0,x0,#1
str x0,&a


(1)从内存中读取a变量到X0寄存器
(2)X0寄存器加1
(3)将X0写入到内存a中

既然是3条指令,那么就有可能并发,也就意味着返回的结果可能不说预期的。

然后在linux kernel的操作系统中,提供访问原子变量的函数,用来解决上述问题。其中部分原子操作的API如下:

  • atomic_read
  • atomic_add_return(i,v)
  • atomic_add(i,v)
  • atomic_inc(v)
  • atomic_add_unless(v,a,u)
  • atomic_inc_not_zero(v)
  • atomic_sub_return(i,v)
  • atomic_sub_and_test(i,v)
  • atomic_sub(i,v)
  • atomic_dec(v)
  • atomic_cmpxchg(v,old,new)

那么操作系统(仅仅是软件而已)是如何保证原子操作的呢?(还是得靠硬件),硬件原理是什么呢?
以上的那些API函数,在底层调用的其实都是如下__lse_atomic_add_return##name宏的封装,这段代码中最核心的也就是ldadd指令了,这是armv8.1增加的LSE(Large System Extension)feature。

(linux/arch/arm64/include/asm/atomic_lse.h)

static inline int __lse_atomic_add_return##name(int i, atomic_t *v)	\
{									\
	u32 tmp;							\
									\
	asm volatile(							\
	__LSE_PREAMBLE							\
	"	ldadd" #mb "	%w[i], %w[tmp], %[v]\n"			\
	"	add	%w[i], %w[i], %w[tmp]"				\
	: [i] "+r" (i), [v] "+Q" (v->counter), [tmp] "=&r" (tmp)	\
	: "r" (v)							\
	: cl);								\
									\
	return i;							\
}

那么系统如果没有LSE扩展呢,即armv8.0,其实现的原型如下所示,这段代码中最核心的也就是ldxrstxr指令了

(linux/arch/arm64/include/asm/atomic_ll_sc.h)

static inline void __ll_sc_atomic_##op(int i, atomic_t *v)\
{									\
	unsigned long tmp;						\
	int result;							\
									\
	asm volatile("// atomic_" #op "\n"				\
	__LL_SC_FALLBACK(						\
"	prfm	pstl1strm, %2\n"					\
"1:	ldxr	%w0, %2\n"						\
"	" #asm_op "	%w0, %w0, %w3\n"				\
"	stxr	%w1, %w0, %2\n"						\
"	cbnz	%w1, 1b\n")						\
	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)		\
	: __stringify(constraint) "r" (i));				\
}

那么在armv8.0之前呢,如armv7是怎样实现的? 如下所示, 这段代码中最核心的也就是ldrexstrex指令了

(linux/arch/arm/include/asm/atomic.h)

static inline void atomic_##op(int i, atomic_t *v)			\
{									\
	unsigned long tmp;						
	int result;							\
									\
	prefetchw(&v->counter);						\
	__asm__ __volatile__("@ atomic_" #op "\n"			\
"1:	ldrex	%0, [%3]\n"						\
"	" #asm_op "	%0, %0, %4\n"					\
"	strex	%1, %0, [%3]\n"						\
"	teq	%1, #0\n"						\
"	bne	1b"							\
	: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)		\
	: "r" (&v->counter), "Ir" (i)					\
	: "cc");							\
}

总结:
在很早期,使用arm的exclusive机制来实现的原子操作,exclusive相关的指令也就是ldrexstrex了,但在armv8后,exclusive机制的指令发生了变化变成了ldxrstxr。但是又由于在一个大系统中,处理器是非常多的,竞争也激烈,使用独占的存储和加载指令可能要多次尝试才能成功,性能也就变得很差,在armv8.1为了解决该问题,增加了ldadd等相关的原子操作指令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码改变世界ctw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值