日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux kernel的spin_lock的详细介绍(以arm64为例)

發(fā)布時(shí)間:2025/3/21 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux kernel的spin_lock的详细介绍(以arm64为例) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1、spin_lock的調(diào)用流程:

static __always_inline void spin_lock(spinlock_t *lock) {raw_spin_lock(&lock->rlock); } #define raw_spin_lock(lock) _raw_spin_lock(lock) void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) __acquires(lock); void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) {__raw_spin_lock(lock); } static inline void __raw_spin_lock(raw_spinlock_t *lock) {preempt_disable(); //&&&&&& 這里是禁止搶占&&&&spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); } static inline int do_raw_spin_trylock(raw_spinlock_t *lock) {return arch_spin_trylock(&(lock)->raw_lock); }static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock) {arch_spin_unlock(&lock->raw_lock); //調(diào)用到arch體系相關(guān)代碼__release(lock); } static inline void arch_spin_lock(arch_spinlock_t *lock) {unsigned int tmp;arch_spinlock_t lockval, newval;asm volatile(/* Atomically increment the next ticket. */ARM64_LSE_ATOMIC_INSN(/* LL/SC */ " prfm pstl1strm, %3\n" //cache相關(guān)指令 "1: ldaxr %w0, %3\n" " add %w1, %w0, %w5\n" " stxr %w2, %w1, %3\n" " cbnz %w2, 1b\n",/* LSE atomics */ " mov %w2, %w5\n" " ldadda %w2, %w0, %3\n" " nop\n" " nop\n" " nop\n")/* Did we get the lock? */ " eor %w1, %w0, %w0, ror #16\n" " cbz %w1, 3f\n"/** No: spin on the owner. Send a local event to avoid missing an* unlock before the exclusive load.*/ " sevl\n" "2: wfe\n" //&&&& 讓core進(jìn)入low-power state " ldaxrh %w2, %4\n" " eor %w1, %w2, %w0, lsr #16\n" " cbnz %w1, 2b\n" //&&&& 這一段是一個(gè)循環(huán),也就是自旋等待/* We got the lock. Critical section starts here. */ "3:": "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock): "Q" (lock->owner), "I" (1 << TICKET_SHIFT): "memory"); }

2、使用場景:
spin_lock:當(dāng)線程A拿了鎖,線程B再去拿鎖,就會失敗(拿不到),線程B就會自旋在哪里,等待鎖釋放.
mutex:當(dāng)線程A拿了鎖,線程B再去拿鎖,就會失敗(拿不到),會陷入sleep, 等到線程A釋放了鎖,線程B才會wakeup,獲得該鎖;
如果鎖住的“事務(wù)”很簡單,占用很少的時(shí)間,就應(yīng)該使用spinlock,這個(gè)時(shí)候spinlock的代價(jià)比mutex會小很多。”事務(wù)”很快執(zhí)行完畢,自旋的消耗遠(yuǎn)遠(yuǎn)小于陷入sleep和wake的消耗

3、問與答:
(1)、spin_lock中,會什么要禁止搶占(preempt_disable)?
(以單核為例)
P1 holds the lock and after a time is scheduled out.
Now P2 starts executing and let’s say requests the same spinlock. Since P1 has not released the spinlock, P2 must spin and do nothing. So the execution time of P2 is wasted. It makes more sense to let P1 release the lock and then allow it to be preempted.
Also since there is no other processor, simply by disallowing preemption we know that there will never be another process that runs in parallel on another processor and accesses the same critical section.
也就是說, process1正在持有該鎖,此時(shí)發(fā)生了schedule后process2又去試圖拿該鎖,process2就會自旋在那里,時(shí)間就浪費(fèi)了. 合理的做法應(yīng)該是,讓Process1(執(zhí)行完臨界區(qū))釋放該鎖

(2)、在這樣的場景下,使用spin lock可以保護(hù)訪問共享資源R的臨界區(qū)嗎?
我們假設(shè)CPU0上的進(jìn)程A持有spin lock進(jìn)入臨界區(qū),這時(shí)候,外設(shè)P發(fā)生了中斷事件,并且調(diào)度到了CPU1上執(zhí)行,看起來沒有什么問題,執(zhí)行在CPU1上的handler會稍微等待一會CPU0上的進(jìn)程A,等它立刻臨界區(qū)就會釋放spin lock的,但是,如果外設(shè)P的中斷事件被調(diào)度到了CPU0上執(zhí)行會怎么樣?CPU0上的進(jìn)程A在持有spin lock的狀態(tài)下被中斷上下文搶占,而搶占它的CPU0上的handler在進(jìn)入臨界區(qū)之前仍然會試圖獲取spin lock,悲劇發(fā)生了,CPU0上的P外設(shè)的中斷handler永遠(yuǎn)的進(jìn)入spin狀態(tài),這時(shí)候,CPU1上的進(jìn)程B也不可避免在試圖持有spin lock的時(shí)候失敗而導(dǎo)致進(jìn)入spin狀態(tài)。為了解決這樣的問題,linux kernel采用了這樣的辦法:如果涉及到中斷上下文的訪問,spin lock需要和禁止本CPU上的中斷聯(lián)合使用

spin lock和禁止本CPU上的中斷聯(lián)合使用:

(3)、假設(shè)只有一個(gè)cpu,如果把spin_lock中的preempt_disable注釋掉, 即允許搶占。 那么使用spin_lock會產(chǎn)生死鎖嗎?
不會。threadA在執(zhí)行時(shí)臨界區(qū)時(shí),被schedule出去了,thread B試圖獲取該鎖,threadB會自旋那里(比較浪費(fèi)cpu資源),等到再次被調(diào)度到threadA并且釋放了該鎖后,threadB才可以繼續(xù)往下跑。

(4)、spinlock的臨界區(qū)為什么不允許sleep(使用schedule類函數(shù))?
Thread A調(diào)用spin_lock進(jìn)去臨界區(qū),此時(shí)該cpu已經(jīng)禁止搶占了(preempt_disable),如果此時(shí)調(diào)用sleep主動(dòng)schedule出去后,該cpu就永遠(yuǎn)回不來了因?yàn)榻箵屨剂恕_@樣的話,如果threadB再試圖獲取該鎖時(shí),就會發(fā)生死鎖。

4、 wfe/sev的使用

(1)、WFI,執(zhí)行WFI指令后,ARM core會立即進(jìn)入low-power standby state,直到有WFI Wakeup events發(fā)生。
(2)、WFE,執(zhí)行WFE指令后,根據(jù)Event Register(一個(gè)單bit的寄存器,每個(gè)PE一個(gè))的狀態(tài),有兩種情況:
a. 如果Event Register為1,該指令會把它清零,然后執(zhí)行完成(不會standby);
b. 如果Event Register為0,和WFI類似,進(jìn)入low-power standby state,直到有WFE Wakeup events發(fā)生。
(3)、SEV指令,就是一個(gè)用來改變Event Register的指令,有兩個(gè):SEV會修改所有PE上的寄存器;SEVL,只修改本PE的寄存器值

WFE應(yīng)用于arch_spin_lock場景:

a)資源空閑 b)Core1訪問資源,acquire lock,獲得資源 c)Core2訪問資源,此時(shí)資源不空閑,執(zhí)行WFE指令,讓core進(jìn)入low-power state d)Core1釋放資源,release lock,釋放資源,同時(shí)會喚醒Core2(unlock中的staddlh會喚醒WFE) e)Core2獲得資源

我們也剖析下現(xiàn)有的代碼:
arch_spin_lock進(jìn)來后,先執(zhí)行sevl (Event Register變成1),再執(zhí)行wfe Event Register變成0),執(zhí)行cbnz如果沒有拿到鎖,再跳轉(zhuǎn)到2處,又執(zhí)行wfe了,此時(shí)cpu進(jìn)入low-power standby state
等到其它的cpu進(jìn)程釋放了該鎖(發(fā)送sev信號后),當(dāng)前cpu退出low-power standby state繼續(xù)往下執(zhí)行,執(zhí)行cbnz獲取到該鎖,繼續(xù)向下執(zhí)行.

問題 : 那么“其它的cpu進(jìn)程釋放了該鎖(發(fā)送sev信號后)”,其它c(diǎn)pu在哪里發(fā)送的sev信號呢???
回答 : 等待自旋鎖的時(shí)候,使用指令ldaxrh(帶有獲取語義的獨(dú)占加載,h表示halfword,即2字節(jié))讀取服務(wù)號,獨(dú)占加載操作會設(shè)置處理器的獨(dú)占監(jiān)視器,記錄鎖的物理地址。
釋放鎖的時(shí)候,使用stlrh指令修改鎖的值,stlrh指令會清除所有監(jiān)視鎖的物理地址的處理器的獨(dú)占監(jiān)視器,清除獨(dú)占監(jiān)視器的時(shí)候會生成一個(gè)喚醒事件
(可以查看armv8文檔中的 “WFE wake-up events in AArch64 state” 章節(jié),看看都是哪些事件或命令可以喚醒WFE,其中里面提到:An event caused by the clearing of the global monitor for the PE)

static inline void arch_spin_lock(arch_spinlock_t *lock) {...... " sevl\n" "2: wfe\n" " ldaxrh %w2, %4\n" " eor %w1, %w2, %w0, lsr #16\n" " cbnz %w1, 2b\n"/* We got the lock. Critical section starts here. */ "3:": "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock): "Q" (lock->owner), "I" (1 << TICKET_SHIFT): "memory"); }

執(zhí)行staddlh會產(chǎn)生一個(gè)WFE的喚醒時(shí)間

static inline void arch_spin_unlock(arch_spinlock_t *lock) {unsigned long tmp;asm volatile(ARM64_LSE_ATOMIC_INSN(/* LL/SC */" ldrh %w1, %0\n"" add %w1, %w1, #1\n"" stlrh %w1, %0",/* LSE atomics */" mov %w1, #1\n"" nop\n"" staddlh %w1, %0"): "=Q" (lock->owner), "=&r" (tmp):: "memory"); }

總結(jié)

以上是生活随笔為你收集整理的linux kernel的spin_lock的详细介绍(以arm64为例)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。