无码国产午夜福利_色窝窝51精品_欧美日本一道本_国产高潮流白浆视频_亚洲伊甸园在线无码

當(dāng)前位置: 首頁 >  綜合 > 正文

世界百事通!Linux內(nèi)核同步機(jī)制spinlock詳解

2023-06-26 16:33:24 來源:嵌入式Linux開發(fā)

Linux內(nèi)核同步機(jī)制spinlock

spin_lock是什么

在平時(shí)的工作中,作為開發(fā)人員經(jīng)常碰到這樣的問題:多線程或多進(jìn)程共享的數(shù)據(jù)如何進(jìn)行保護(hù),如果發(fā)生進(jìn)程上下文切換或中斷上下文切換都可能使共享數(shù)據(jù)發(fā)生爭搶問題。這時(shí)候就可以考慮用鎖了。如果是進(jìn)程上下文切換引起的可以考慮用信號量或mutex互斥鎖,但如果發(fā)生在中斷上下文,這時(shí)候信號量和mutex就無法使用了,因?yàn)檫@兩種鎖機(jī)制是可以睡眠的,而中斷上下文又禁止睡眠,這時(shí),spin_lock就是我們最好的選擇了。


(資料圖片僅供參考)

spin_lock是一種死等的鎖機(jī)制。當(dāng)發(fā)生資源訪問沖突的時(shí)候,可以有兩個(gè)選擇:一個(gè)是死等,一個(gè)是掛起當(dāng)前進(jìn)程,調(diào)度其他進(jìn)程執(zhí)行。spin_lock一次只允許一個(gè)線程進(jìn)入共享資源區(qū),信號量可以允許多個(gè)線程進(jìn)入。spin_lock執(zhí)行的時(shí)間要短,因?yàn)閟pin_lock是死等鎖,所以在共享資源區(qū)的執(zhí)行時(shí)間不能太長,否則會造成CPU的資源浪費(fèi)。spin_lock最重要的一點(diǎn)是可以在中斷上下文執(zhí)行。

資源競爭

現(xiàn)代CPU一般都是多核多CPU的SMP架構(gòu),在這種情況下就有可能出現(xiàn)多個(gè)CPU要共同訪問同一個(gè)資源的問題,此時(shí)就需要對資源進(jìn)行保護(hù),確保同一個(gè)時(shí)刻只有一個(gè)CPU正在訪問修改資源變量。鎖就是在這種背景下誕生的,鎖的種類有很多,應(yīng)用場景也不同,本篇我們主要介紹spinlock自旋鎖。

進(jìn)程上下文

如果一個(gè)全局的資源被多個(gè)進(jìn)程上下文訪問,此時(shí),內(nèi)核是如何執(zhí)行的呢?對于沒有開啟內(nèi)核可搶占選項(xiàng)的內(nèi)核,所有的系統(tǒng)調(diào)用都是串行執(zhí)行的,并不存在資源競爭的問題。但是。對于現(xiàn)在的大部分內(nèi)核來說可搶占選項(xiàng)是開啟的。

假如現(xiàn)在有一個(gè)共享資源S,有兩個(gè)進(jìn)程A和B,都需要訪問共享資源S

執(zhí)行流程大概是這樣的:

進(jìn)程A訪問資源S的時(shí)候發(fā)生了中斷,中斷去喚醒優(yōu)先級更高的進(jìn)程B,中斷返回的時(shí)候,發(fā)生進(jìn)程切換,優(yōu)先級更高的進(jìn)程B執(zhí)行,進(jìn)程B訪問共享資源S,如果沒有加鎖保護(hù),此時(shí)進(jìn)程A和進(jìn)程B都訪問了共享資源S,導(dǎo)致程序的執(zhí)行結(jié)果不正確。如果通過spin_lock加鎖保護(hù),進(jìn)程A訪問共享資源S前獲取spin_lock,此時(shí)發(fā)生了中斷,優(yōu)先級更高的進(jìn)程B開始執(zhí)行,進(jìn)程B會去獲取spin_lock,由于spin_lock被進(jìn)程A所持有,導(dǎo)致進(jìn)程B獲取spin_lock失敗,進(jìn)程B會死等直到進(jìn)程A釋放了spin_lock,然后進(jìn)程B就可以獲取spin_lock,訪問共享資源S。

中斷上下文

進(jìn)程A運(yùn)行在CPU0上訪問共享資源S進(jìn)程B運(yùn)行在CPU1上訪問共享資源S外部設(shè)備發(fā)生中斷訪問共享資源S 此時(shí)進(jìn)程A在CPU0上獲取spin_lock開始訪問共享資源S,這時(shí)外部設(shè)備發(fā)生了中斷,并且在CPU1上執(zhí)行,假如中斷執(zhí)行了一段時(shí)間后被調(diào)度到了CPU0上執(zhí)行,此時(shí)會怎樣呢?由于CPU0上進(jìn)程A已經(jīng)獲取了spin_lock,現(xiàn)在被中斷上下文打斷,中斷上下文也要獲取spin_lock,那么現(xiàn)在就進(jìn)入了死胡同也就是死鎖狀態(tài)。而進(jìn)程B在CPU1上也要獲取spin_lock,也會進(jìn)入死鎖狀態(tài)。所有中斷上下文的切換CPU必須禁止本CPU上的中斷。

實(shí)現(xiàn)原理

spinlock結(jié)構(gòu)體定義在頭文件include/linux/spinlock_types.h

typedef struct spinlock { union {  struct raw_spinlock rlock;#ifdef CONFIG_DEBUG_LOCK_ALLOC# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))  struct {   u8 __padding[LOCK_PADSIZE];   struct lockdep_map dep_map;  };#endif };} spinlock_t;

spinlock結(jié)構(gòu)體變量的定義有兩種,一種是靜態(tài)定義,一種是動態(tài)定義。

#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)  //靜態(tài)定義spinlock_t  lock;spin_lock_init (&lock);  //動態(tài)定義

spinlock接口函數(shù)介紹,這些函數(shù)是驅(qū)動編程內(nèi)核編程的時(shí)候會用到的。

位于include/linux/spinlock.h頭文件中static__always_inline raw_spinlock_t *spinlock_check(spinlock_t *lock){ return &lock- >rlock;}#define spin_lock_init(_lock)    \\do {       \\ spinlock_check(_lock);    \\ raw_spin_lock_init(&(_lock)- >rlock);  \\} while (0)static __always_inline void spin_lock(spinlock_t *lock){ raw_spin_lock(&lock- >rlock);}static __always_inline void spin_lock_bh(spinlock_t *lock){ raw_spin_lock_bh(&lock- >rlock);}static __always_inline int spin_trylock(spinlock_t *lock){ return raw_spin_trylock(&lock- >rlock);}#define spin_lock_nested(lock, subclass)   \\do {        \\ raw_spin_lock_nested(spinlock_check(lock), subclass); \\} while (0)#define spin_lock_nest_lock(lock, nest_lock)    \\do {         \\ raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock); \\} while (0)static __always_inline void spin_lock_irq(spinlock_t *lock){ raw_spin_lock_irq(&lock- >rlock);}#define spin_lock_irqsave(lock, flags)    \\do {        \\ raw_spin_lock_irqsave(spinlock_check(lock), flags); \\} while (0)#define spin_lock_irqsave_nested(lock, flags, subclass)   \\do {         \\ raw_spin_lock_irqsave_nested(spinlock_check(lock), flags, subclass); \\} while (0)static __always_inline void spin_unlock(spinlock_t *lock){ raw_spin_unlock(&lock- >rlock);}static __always_inline void spin_unlock_bh(spinlock_t *lock){ raw_spin_unlock_bh(&lock- >rlock);}static __always_inline void spin_unlock_irq(spinlock_t *lock){ raw_spin_unlock_irq(&lock- >rlock);}static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags){ raw_spin_unlock_irqrestore(&lock- >rlock, flags);}static __always_inline int spin_trylock_bh(spinlock_t *lock){ return raw_spin_trylock_bh(&lock- >rlock);}static __always_inline int spin_trylock_irq(spinlock_t *lock){ return raw_spin_trylock_irq(&lock- >rlock);}#define spin_trylock_irqsave(lock, flags)   \\({        \\ raw_spin_trylock_irqsave(spinlock_check(lock), flags); \\})static __always_inline int spin_is_locked(spinlock_t *lock){ return raw_spin_is_locked(&lock- >rlock);}static __always_inline int spin_is_contended(spinlock_t *lock){ return raw_spin_is_contended(&lock- >rlock);}static __always_inline int spin_can_lock(spinlock_t *lock){ return raw_spin_can_lock(&lock- >rlock);}#define assert_spin_locked(lock) assert_raw_spin_locked(&(lock)- >rlock)

spinlock底層實(shí)現(xiàn)源碼分析

spin_lock()函數(shù)調(diào)用raw_spin_lock()函數(shù),傳入raw_spinlock_t指針變量。

static __always_inline void spin_lock(spinlock_t *lock){ raw_spin_lock(&lock- >rlock);}

raw_spin_lock()宏定義函數(shù)就是_raw_spin_lock()宏定義函數(shù)。

#define raw_spin_lock(lock) _raw_spin_lock(lock)

_raw_spin_lock()函數(shù)調(diào)用__raw_spin_lock()函數(shù),并傳入raw_spinlock_t指針變量。

void __lockfunc _raw_spin_lock(raw_spinlock_t *lock){ __raw_spin_lock(lock);}

__raw_spin_lock()函數(shù)調(diào)用preempt_disable()函數(shù)禁止內(nèi)核搶占,調(diào)用spin_acquire()函數(shù)進(jìn)行運(yùn)行時(shí)檢查鎖的有效性,調(diào)用LOCK_CONTENDED()實(shí)際調(diào)用的就是do_raw_spin_lock()函數(shù),并傳入raw_spinlock_t指針變量。

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);}#define LOCK_CONTENDED(_lock, try, lock) \\ lock(_lock)

do_raw_spin_lock()函數(shù)調(diào)用debug_spin_lock_before()函數(shù)進(jìn)行靜態(tài)檢查,調(diào)用arch_spin_lock()函數(shù)具體實(shí)現(xiàn)針對arm體系架構(gòu)的自旋鎖機(jī)制,調(diào)用debug_spin_lock_after()函數(shù)進(jìn)行靜態(tài)檢查。

void do_raw_spin_lock(raw_spinlock_t *lock){ debug_spin_lock_before(lock); arch_spin_lock(&lock- >raw_lock); debug_spin_lock_after(lock);}

arch_spin_lock()函數(shù)調(diào)用prefetchw()函數(shù)讀取鎖變量的值,該函數(shù)和cache有關(guān),主要是為了提高性能,接下來是一段內(nèi)嵌匯編代碼,主要是實(shí)現(xiàn)原子操作,通過ldrex和strex這兩個(gè)獨(dú)占訪問指令實(shí)現(xiàn)。接下來判斷當(dāng)前的spinlock是否已經(jīng)被持有,如果已經(jīng)被持有則調(diào)用wfe()函數(shù)進(jìn)入等待狀態(tài),WFE(Wait for event)是讓ARM核進(jìn)入低功耗待機(jī)模式的指令,由ARM架構(gòu)規(guī)范定義,由ARM核實(shí)現(xiàn)。接著判斷本CPU是否是其它CPU喚醒執(zhí)行的,如果是則說明owner的值發(fā)生了變化,將新的owner值賦給lockval。smp_mb是memory barrier相關(guān)的操作。

static inline void arch_spin_lock(arch_spinlock_t *lock){ unsigned long tmp; u32 newval; arch_spinlock_t lockval; prefetchw(&lock- >slock); __asm__ __volatile__("1: ldrex %0, [%3]\\n"" add %1, %0, %4\\n"" strex %2, %1, [%3]\\n"" teq %2, #0\\n"" bne 1b" : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) : "r" (&lock- >slock), "I" (1 < < TICKET_SHIFT) : "cc"); while (lockval.tickets.next != lockval.tickets.owner) {  wfe();  lockval.tickets.owner = ACCESS_ONCE(lock- >tickets.owner); } smp_mb();}

總結(jié)

本篇主要介紹了Linux內(nèi)核同步機(jī)制spinlock的原理并分析了底層源碼是怎么實(shí)現(xiàn)的,以及spinlock的使用場景。

標(biāo)簽:

<  上一篇

深藍(lán)S7正式上市 售價(jià)14.99萬元起

下一篇 >

最后一頁