当前位置:首页>Linux>Linux Kernel(armv8-aarch64) 的原子操作的底层实现

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

  • 2026-10-11 05:52:31
Linux Kernel(armv8-aarch64) 的原子操作的底层实现

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

ldrx0, &aaddx0,x0,#1strx0,&a

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

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

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

atomic_readatomic_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)staticinlineint __lse_atomic_add_return##name(int i, atomic_t *v)	\{									\	u32 tmp;							\									\asmvolatile(							\	__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,其实现的原型如下所示,这段代码中最核心的也就是ldxr、stxr指令了

(linux/arch/arm64/include/asm/atomic_ll_sc.h)staticinlinevoid __ll_sc_atomic_##op(int i, atomic_t *v)\{									\unsignedlong tmp;						\int result;							\									\asmvolatile("// 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 是怎样实现的?如下所示, 这段代码中最核心的也就是ldrex、strex指令了

(linux/arch/arm/include/asm/atomic.h)staticinlinevoid atomic_##op(int i, atomic_t *v)			\{									\unsignedlong 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 相关的指令也就是ldrex、strex了,但在 armv8 后,exclusive 机制的指令发生了变化变成了ldxr、stxr。但是又由于在一个大系统中,处理器是非常多的,竞争也激烈,使用独占的存储和加载指令可能要多次尝试才能成功,性能也就变得很差,在 armv8.1 为了解决该问题,增加了ldadd等相关的原子操作指令。

  • 如需进群可加我微信邀请进群交流:sami01_2023

最新文章

随机文章