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。
那么系統如果沒有LSE擴展呢,即armv8.0,其實現的原型如下所示,這段代碼中最核心的也就是ldxr、stxr指令了
(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是怎樣實現的? 如下所示, 這段代碼中最核心的也就是ldrex、strex指令了
(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相關的指令也就是ldrex、strex了,但在armv8后,exclusive機制的指令發生了變化變成了ldxr、stxr。但是又由于在一個大系統中,處理器是非常多的,競爭也激烈,使用獨占的存儲和加載指令可能要多次嘗試才能成功,性能也就變得很差,在armv8.1為了解決該問題,增加了ldadd等相關的原子操作指令
總結
以上是生活随笔為你收集整理的02-Linux Kernel(armv8-aarch64)的原子操作的底层实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux Kernel中的同步机制的介
- 下一篇: Linux Kernel中local_i