elasticsearch入門系列">elasticsearch入門系列
946
2022-05-29
LiteOS內(nèi)核源碼分析系列二 SpinLock自旋鎖及LockDep死鎖檢測(cè)
2020年12月發(fā)布的LiteOS 5.0推出了全新的內(nèi)核,支持SMP多核調(diào)度功能。想學(xué)習(xí)SMP多核調(diào)度功能,需要了解下SpinLock自旋鎖。除了多核的自旋鎖機(jī)制,本文還會(huì)介紹下LiteOS 5.0引入的LockDep死鎖檢測(cè)特性。
本文中所涉及的LiteOS源碼,均可以在LiteOS開(kāi)源站點(diǎn)https://gitee.com/LiteOS/LiteOS?獲取。
自旋鎖SpinLock源代碼、開(kāi)發(fā)文檔,LockDep死鎖檢測(cè)特性代碼文檔列表如下:
kernel\include\los_spinlock.h 自旋鎖頭文件
網(wǎng)頁(yè)獲取自旋鎖源碼?https://gitee.com/LiteOS/LiteOS/blob/master/kernel/include/los_spinlock.h。
spinlock.S、arch/spinlock.h 自旋鎖匯編代碼文件及頭文件
針對(duì)不同的CPU架構(gòu),有兩套代碼。由于自旋鎖適用于多核,M核架構(gòu)arch\arm\cortex_m下不包含自旋鎖的匯編文件。如下:
arch\arm\cortex_a_r架構(gòu)
匯編代碼文件
https://gitee.com/LiteOS/LiteOS/blob/master/arch/arm/cortex_a_r/src/spinlock.S。
頭文件
https://gitee.com/LiteOS/LiteOS/blob/master/arch/arm/cortex_a_r/include/arch/spinlock.h。
arch\arm64架構(gòu)
匯編代碼文件?https://gitee.com/LiteOS/LiteOS/blob/master/arch/arm64/src/spinlock.S。
頭文件
https://gitee.com/LiteOS/LiteOS/blob/master/arch/arm64/include/arch/spinlock.h。
開(kāi)發(fā)指南自旋鎖文檔
在線文檔
https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E8%87%AA%E6%97%8B%E9%94%81。
LockDep死鎖檢測(cè)
死鎖檢測(cè)代碼包含:
頭文件
https://gitee.com/LiteOS/LiteOS/blob/master/kernel/include/los_lockdep.h
C代碼文件
https://gitee.com/LiteOS/LiteOS/blob/master/kernel/base/los_lockdep.c。
我們首先來(lái)看看自旋鎖。
1、SpinLock 自旋鎖
在多核環(huán)境中,由于使用相同的內(nèi)存空間,存在對(duì)同一資源進(jìn)行訪問(wèn)的情況,所以需要互斥訪問(wèn)機(jī)制來(lái)保證同一時(shí)刻只有一個(gè)核進(jìn)行操作。自旋鎖就是這樣的一種機(jī)制。
自旋鎖是指當(dāng)一個(gè)線程在獲取鎖時(shí),如果鎖已經(jīng)被其它線程獲取,那么該線程將循環(huán)等待,并不斷判斷是否能夠成功獲取鎖,直到獲取到鎖才會(huì)退出循環(huán)。因此建議保護(hù)耗時(shí)較短的操作,防止對(duì)系統(tǒng)整體性能有明顯的影響。
自旋鎖與互斥鎖比較類似,它們都是為了解決對(duì)共享資源的互斥使用問(wèn)題。無(wú)論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)持有者。但是兩者在調(diào)度機(jī)制上略有不同,對(duì)于互斥鎖,如果鎖已經(jīng)被占用,鎖申請(qǐng)者會(huì)被阻塞;但是自旋鎖不會(huì)引起調(diào)用者阻塞,會(huì)一直循環(huán)檢測(cè)自旋鎖是否已經(jīng)被釋放。自旋鎖用于多核不同CPU核對(duì)資源的互斥訪問(wèn),互斥鎖用于同一CPU核內(nèi)不同任務(wù)對(duì)資源的互斥訪問(wèn)。
自旋鎖SpinLock核心的代碼都在kernel\include\los_spinlock.h頭文件中,包含struct Spinlock結(jié)構(gòu)體定義、一些inline內(nèi)聯(lián)函數(shù)LOS_SpinXXX,還有一些LockDep死鎖檢測(cè)相關(guān)的宏定義LOCKDEP_XXXX。
1.1 Spinlock 自旋鎖結(jié)構(gòu)體
自旋鎖結(jié)構(gòu)體Spinlock定義如下,主要的成員變量為size_t rawLock,這是自旋鎖是否占用持有的成功的標(biāo)記:為0時(shí),鎖沒(méi)有被持有,為1時(shí)表示被成功持有。當(dāng)開(kāi)啟LockDep死鎖檢測(cè)調(diào)測(cè)特性時(shí),會(huì)使能另外3個(gè)成員變量,記錄持有自旋鎖的CPU核信息、任務(wù)信息。
struct Spinlock { size_t rawLock; /**< 原始自旋鎖 */ #ifdef LOSCFG_KERNEL_SMP_LOCKDEP UINT32 cpuid; /**< 死鎖檢測(cè)特性開(kāi)啟時(shí),持有自旋鎖的CPU核 */ VOID *owner; /**< 死鎖檢測(cè)特性開(kāi)啟時(shí),持有自旋鎖的任務(wù)的TCB指針 */ const CHAR *name; /**< 死鎖檢測(cè)特性開(kāi)啟時(shí),持有自旋鎖的任務(wù)的名稱 */ #endif };
1.2 Spinlock 自旋鎖常用函數(shù)接口
LiteOS自旋鎖模塊為用戶提供下面幾種功能,包含自旋鎖初始化,申請(qǐng)/釋放,查詢自旋鎖狀態(tài)等。自旋鎖相關(guān)的函數(shù)、宏定義只支持SMP - Symmetric MultiProcessor模式,當(dāng)單核UP - UniProcessor時(shí),函數(shù)不生效。接口詳細(xì)信息可以查看API參考。
1.2.1 自旋鎖初始化
自旋鎖初始化的內(nèi)聯(lián)函數(shù)如下,其中參數(shù)SPIN_LOCK_S *lock,即自旋鎖結(jié)構(gòu)體指針,其中SPIN_LOCK_S是Spinlock的typedef別名,在kernel\include\los_lockdep.h文件中定義的。
自旋鎖初始時(shí),會(huì)把自旋鎖標(biāo)記為0:lock->rawLock = 0,當(dāng)開(kāi)啟死鎖檢測(cè)特性時(shí),也會(huì)做相應(yīng)的初始化。
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_SpinInit(SPIN_LOCK_S *lock) { lock->rawLock = 0; #ifdef LOSCFG_KERNEL_SMP_LOCKDEP lock->cpuid = (UINT32)-1; lock->owner = SPINLOCK_OWNER_INIT; lock->name = "spinlock"; #endif }
LOS_SpinInit()是動(dòng)態(tài)初始化的自旋鎖,LiteOS還提供了靜態(tài)初始化自旋鎖的方法SPIN_LOCK_INIT(lock):
#define SPIN_LOCK_INIT(lock) SPIN_LOCK_S lock = SPIN_LOCK_INITIALIZER(lock)
1.2.2 申請(qǐng)/釋放自旋鎖
初始化自旋鎖后,可以以SPIN_LOCK_S *lock為參數(shù)申請(qǐng)、釋放自旋鎖。自旋鎖的這些函數(shù)中,調(diào)用的LOCKDEP_開(kāi)頭函數(shù)是死鎖檢測(cè)的函數(shù),后文會(huì)詳細(xì)講述。核心的3個(gè)函數(shù)由匯編語(yǔ)言編寫(xiě),這些匯編函數(shù)存,根據(jù)不同的CPU架構(gòu),可以在文件arch\arm\cortex_a_r\src\spinlock.S或arch\arm64\src\spinlock.S中查看,此文不再詳細(xì)講述其匯編代碼。
ArchSpinLock(&lock->rawLock); // 匯編語(yǔ)言編寫(xiě)的 申請(qǐng)自旋鎖的函數(shù) ArchSpinUnlock(&lock->rawLock); // 匯編語(yǔ)言編寫(xiě)的 釋放自旋鎖的函數(shù) ArchSpinTrylock(&lock->rawLock); // 匯編語(yǔ)言編寫(xiě)的 嘗試申請(qǐng)自旋鎖的函數(shù)
STATIC INLINE VOID LOS_SpinLock(SPIN_LOCK_S *lock) 申請(qǐng)自旋鎖
該函數(shù)嘗試申請(qǐng)自旋鎖,如果自旋鎖鎖被其他核占用,則循環(huán)等待,直至其他核釋放自旋鎖。
我們看下代碼首先執(zhí)行⑴處代碼,暫停任務(wù)調(diào)度,然后執(zhí)行匯編函數(shù)ArchSpinLock(&lock->rawLock)申請(qǐng)自旋鎖。
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_SpinLock(SPIN_LOCK_S *lock) { ⑴ LOS_TaskLock(); LOCKDEP_CHECK_IN(lock); ⑵ ArchSpinLock(&lock->rawLock); LOCKDEP_RECORD(lock); }
STATIC INLINE VOID LOS_SpinUnlock(SPIN_LOCK_S *lock) 釋放自旋鎖
釋放自旋鎖LOS_SpinUnlock(SPIN_LOCK_S *lock)需要和申請(qǐng)自旋鎖的函數(shù)LOS_SpinLock(SPIN_LOCK_S *lock)成對(duì)使用。執(zhí)行⑴處匯編函數(shù)ArchSpinUnlock(&lock->rawLock)釋放自旋鎖,然后執(zhí)行⑵恢復(fù)任務(wù)調(diào)度功能。
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_SpinUnlock(SPIN_LOCK_S *lock) { LOCKDEP_CHECK_OUT(lock); ⑴ ArchSpinUnlock(&lock->rawLock); ⑵ LOS_TaskUnlock(); }
STATIC INLINE INT32 LOS_SpinTrylock(SPIN_LOCK_S *lock) 嘗試申請(qǐng)自旋鎖
嘗試申請(qǐng)指定的自旋鎖,如果無(wú)法獲取鎖,直接返回失敗,而不會(huì)一直循環(huán)等待。用戶根據(jù)返回值,判斷是否成功申請(qǐng)到自旋鎖,然后再做后續(xù)業(yè)務(wù)處理。和函數(shù)LOS_SpinLock(SPIN_LOCK_S *lock)執(zhí)行的匯編函數(shù)不同,該函數(shù)調(diào)用的匯編函數(shù)為ArchSpinTrylock(&lock->rawLock),并有返回值。
LITE_OS_SEC_ALW_INLINE STATIC INLINE INT32 LOS_SpinTrylock(SPIN_LOCK_S *lock) { LOS_TaskLock(); LOCKDEP_CHECK_IN(lock); ⑴ INT32 ret = ArchSpinTrylock(&lock->rawLock); if (ret == LOS_OK) { LOCKDEP_RECORD(lock); } return ret; }
1.2.3 申請(qǐng)/釋放自旋鎖(同時(shí)進(jìn)行關(guān)中斷保護(hù))
LiteOS?還提供一對(duì)支持關(guān)中斷保護(hù)的申請(qǐng)/釋放指定自旋鎖的函數(shù),除了參數(shù)SPIN_LOCK_S *lock,還需要參數(shù)UINT32 *intSave用于關(guān)中斷、恢復(fù)中斷。LOS_SpinLockSave()和LOS_SpinUnlockRestore()必須成對(duì)使用。
STATIC INLINE VOID LOS_SpinLockSave(SPIN_LOCK_S *lock, UINT32 *intSave) 關(guān)中斷后,再申請(qǐng)指定的自旋鎖
值
從代碼中,可以看出首先執(zhí)行LOS_IntLock()關(guān)中斷,然后再調(diào)用LOS_SpinLock(lock)申請(qǐng)自旋鎖。
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_SpinLockSave(SPIN_LOCK_S *lock, UINT32 *intSave) { *intSave = LOS_IntLock(); LOS_SpinLock(lock); }
STATIC INLINE VOID LOS_SpinUnlockRestore(SPIN_LOCK_S *lock, UINT32 *intSave) 關(guān)中斷后,再申請(qǐng)指定的自旋鎖
值。
從代碼中,可以看出首先調(diào)用LOS_SpinUnlock(lock)釋放自旋鎖,然后再調(diào)用LOS_IntRestore(intSave)恢復(fù)中斷。
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_SpinUnlockRestore(SPIN_LOCK_S *lock, UINT32 intSave) { LOS_SpinUnlock(lock); LOS_IntRestore(intSave); }
1.2.4 獲取自旋鎖持有狀態(tài)
可以使用函數(shù)BOOL LOS_SpinHeld(const SPIN_LOCK_S *lock)查詢自旋鎖的持有狀態(tài),返回TRUE,自旋鎖鎖被持有,返回FALSE時(shí)表示沒(méi)有被持有:
LITE_OS_SEC_ALW_INLINE STATIC INLINE BOOL LOS_SpinHeld(const SPIN_LOCK_S *lock) { return (lock->rawLock != 0); }
2、LockDep 死鎖檢測(cè)調(diào)測(cè)特性
LockDep是Lock Dependency Check的縮寫(xiě),是內(nèi)核的一種死鎖檢測(cè)機(jī)制。這個(gè)調(diào)測(cè)特性默認(rèn)是關(guān)閉的,如果需要該調(diào)測(cè)特性,需要使能宏定義LOSCFG_KERNEL_SMP_LOCKDEP。當(dāng)檢測(cè)到死鎖錯(cuò)誤時(shí),會(huì)打印發(fā)生死鎖的自旋鎖的相關(guān)信息,打印backtrace回溯棧信息。
2.1 LockDep 自旋鎖的錯(cuò)誤類型及結(jié)構(gòu)體定義
在文件kernel\include\los_lockdep.h中定義了死鎖的枚舉類型LockDepErrType及HeldLocks結(jié)構(gòu)體。
自旋鎖的錯(cuò)誤類型有double lock重復(fù)申請(qǐng)鎖、dead lock死鎖、unlock without lock釋放未持有的鎖、lockdep overflow死鎖檢測(cè)溢出,超出定義的MAX_LOCK_DEPTH。
結(jié)構(gòu)體LockDep是任務(wù)LosTaskCB結(jié)構(gòu)體的開(kāi)啟LOSCFG_KERNEL_SMP_LOCKDEP時(shí)的一個(gè)成員變量,記錄該任務(wù)持有的自旋鎖、需要申請(qǐng)的自旋鎖的信息。結(jié)構(gòu)體HeldLocks記錄持有的自旋鎖的詳細(xì)信息,各個(gè)成員變量見(jiàn)如下注釋:
typedef struct Spinlock SPIN_LOCK_S; #define MAX_LOCK_DEPTH 16U enum LockDepErrType { LOCKDEP_SUCEESS = 0, LOCKDEP_ERR_DOUBLE_LOCK, // double lock 重復(fù)申請(qǐng)鎖 LOCKDEP_ERR_DEAD_LOCK, // dead lock 死鎖 LOCKDEP_ERR_UNLOCK_WITHOUT_LOCK, // unlock without lock 釋放未持有的鎖 LOCKDEP_ERR_OVERFLOW, // lockdep overflow 死鎖檢測(cè)溢出 }; typedef struct { VOID *lockPtr; // Spinlock 自旋鎖的內(nèi)存地址 VOID *lockAddr; // 請(qǐng)求鎖的函數(shù)的返回地址 UINT64 waitTime; // 搶占申請(qǐng)自旋鎖的等待時(shí)間 UINT64 holdTime; // 持有自旋鎖的時(shí)間 } HeldLocks; typedef struct { VOID *waitLock; // 任務(wù)申請(qǐng)占用的自旋鎖Spinlock INT32 lockDepth; // 自旋鎖的深度 HeldLocks heldLocks[MAX_LOCK_DEPTH]; // 持有的自旋鎖詳細(xì)信息數(shù)組 } LockDep;
2.2 LockDep 死鎖檢測(cè)的常用函數(shù)接口
LockDep?死鎖檢測(cè)特性提供了3個(gè)函數(shù)接口,在申請(qǐng)自旋鎖前、成功申請(qǐng)到自旋鎖后、釋放自旋鎖后打點(diǎn)調(diào)用。另外,提供了一些其他常用函數(shù)接口。
我們先看下,死鎖檢測(cè)函數(shù)如何記錄等待時(shí)間waitTime、持有時(shí)間holdTime的。在申請(qǐng)自旋鎖前調(diào)用OsLockDepCheckIn(),記錄waitTime的起點(diǎn);成功申請(qǐng)到自旋鎖后,調(diào)用OsLockDepRecord()記錄waitTime的結(jié)束點(diǎn),同時(shí)記錄記錄holdTime的起點(diǎn);釋放自旋鎖后調(diào)用OsLockDepCheckOut()記錄holdTime的結(jié)束點(diǎn)。如圖所示:
2.2.1 OsLockDepCheckIn(const SPIN_LOCK_S *lock) 記錄申請(qǐng)自旋鎖
我們一起分析下代碼,看看申請(qǐng)自旋鎖前死鎖檢測(cè)特性做了哪些操作。⑴處代碼獲取請(qǐng)求自旋鎖的函數(shù)返回地址。⑵獲取當(dāng)前任務(wù)的TCB,然后獲取它的死鎖檢測(cè)成員LockDep *lockDep。⑶、⑽處兩個(gè)函數(shù)配對(duì)使用,前者先關(guān)中斷,然后等待、占用死鎖檢測(cè)特性、設(shè)置STATIC Atomic g_lockdepAvailable為0,后者釋放鎖檢測(cè)特性,設(shè)置STATIC Atomic g_lockdepAvailable為1,然后恢復(fù)中斷。
⑷處代碼判斷當(dāng)前任務(wù)持有的自旋鎖是否超過(guò)死鎖檢測(cè)特性設(shè)置的自旋鎖數(shù)量的最大值MAX_LOCK_DEPTH,如果超過(guò),則報(bào)溢出錯(cuò)誤,跳轉(zhuǎn)到OUT繼續(xù)執(zhí)行。⑸處代碼,如果申請(qǐng)的自旋鎖沒(méi)有被任何CPU核持有,可以直接占有,無(wú)需等待,跳轉(zhuǎn)到OUT繼續(xù)執(zhí)行。⑹處代碼,如果申請(qǐng)的自旋鎖被當(dāng)前任務(wù)持有,則報(bào)重復(fù)申請(qǐng)自旋鎖錯(cuò)誤,跳轉(zhuǎn)到OUT繼續(xù)執(zhí)行。⑺處判斷是否發(fā)生死鎖,稍后再分析函數(shù)OsLockDepCheckDependancy()。
⑻處代碼,如果檢測(cè)結(jié)果通過(guò),可以持有自旋鎖,則記錄相關(guān)信息,包含要申請(qǐng)的自旋鎖、申請(qǐng)鎖的函數(shù)返回地址、申請(qǐng)自旋鎖的開(kāi)始時(shí)間。否則執(zhí)行⑼處代碼,輸出死鎖錯(cuò)誤信息。
VOID OsLockDepCheckIn(const SPIN_LOCK_S *lock) { UINT32 intSave; enum LockDepErrType checkResult = LOCKDEP_SUCEESS; ⑴ VOID *requestAddr = (VOID *)__builtin_return_address(0); ⑵ LosTaskCB *current = OsCurrTaskGet(); LockDep *lockDep = ¤t->lockDep; LosTaskCB *lockOwner = NULL; if (lock == NULL) { return; } ⑶ OsLockDepRequire(&intSave); ⑷ if (lockDep->lockDepth >= (INT32)MAX_LOCK_DEPTH) { checkResult = LOCKDEP_ERR_OVERFLOW; goto OUT; } lockOwner = lock->owner; ⑸ if (lockOwner == SPINLOCK_OWNER_INIT) { goto OUT; } ⑹ if (current == lockOwner) { checkResult = LOCKDEP_ERR_DOUBLE_LOCK; goto OUT; } ⑺ if (OsLockDepCheckDependancy(current, lockOwner) != TRUE) { checkResult = LOCKDEP_ERR_DEAD_LOCK; goto OUT; } OUT: ⑻ if (checkResult == LOCKDEP_SUCEESS) { lockDep->waitLock = (SPIN_LOCK_S *)lock; lockDep->heldLocks[lockDep->lockDepth].lockAddr = requestAddr; lockDep->heldLocks[lockDep->lockDepth].waitTime = OsLockDepGetCycles(); /* start time */ } else { ⑼ OsLockDepDumpLock(current, lock, requestAddr, checkResult); } ⑽ OsLockDepRelease(intSave); }
我們?cè)俜治鱿滤梨i檢測(cè)的函數(shù)OsLockDepCheckDependancy(),循環(huán)判斷嵌套申請(qǐng)的自旋鎖是否會(huì)發(fā)生死鎖,包含2個(gè)參數(shù),第一個(gè)參數(shù)是申請(qǐng)自旋鎖的任務(wù)LosTaskCB *current,第二個(gè)參數(shù)為持有自旋鎖的任務(wù)LosTaskCB *lockOwner:
⑴處代碼,如果申請(qǐng)自旋鎖的任務(wù)和持有鎖的任務(wù)同一個(gè),則發(fā)生死鎖。⑵處代碼,如果持有自旋鎖的任務(wù),還在申請(qǐng)其他自旋鎖,則把lockOwner指向其他自旋鎖的任務(wù)TCB,否則退出循環(huán)。⑶如果自旋鎖被占用則一直循環(huán)。
STATIC BOOL OsLockDepCheckDependancy(const LosTaskCB *current, const LosTaskCB *lockOwner) { BOOL checkResult = TRUE; const SPIN_LOCK_S *lockTemp = NULL; do { ⑴ if (current == lockOwner) { checkResult = FALSE; return checkResult; } ⑵ if (lockOwner->lockDep.waitLock != NULL) { lockTemp = lockOwner->lockDep.waitLock; lockOwner = lockTemp->owner; } else { break; } ⑶ } while (lockOwner != SPINLOCK_OWNER_INIT); return checkResult; }
死鎖檢測(cè)TCB、LockDep、Spinlock關(guān)系示意圖:
2.2.2 OsLockDepRecord(const SPIN_LOCK_S *lock) 記錄申請(qǐng)到的自旋鎖
我們繼續(xù)分析,當(dāng)申請(qǐng)自旋鎖后,死鎖檢測(cè)特性做了哪些操作。⑴處代碼獲取系統(tǒng)運(yùn)行以來(lái)的cycle數(shù)目,然后計(jì)算waitTime,即從開(kāi)始申請(qǐng)自旋鎖到申請(qǐng)到自旋鎖之前的cycle數(shù)目,同時(shí)記錄持有自旋鎖的holdTime的開(kāi)始時(shí)間。⑵處代碼更新自旋鎖的信息,鎖被當(dāng)前任務(wù)持有,CPU核設(shè)置為當(dāng)前核。⑶處更新死鎖檢測(cè)lockDep的信息,持有鎖的數(shù)目加1,等待鎖置空。
VOID OsLockDepRecord(SPIN_LOCK_S *lock) { UINT32 intSave; UINT64 cycles; LosTaskCB *current = OsCurrTaskGet(); LockDep *lockDep = ¤t->lockDep; HeldLocks *heldlock = &lockDep->heldLocks[lockDep->lockDepth]; if (lock == NULL) { return; } OsLockDepRequire(&intSave); ⑴ cycles = OsLockDepGetCycles(); heldlock->waitTime = cycles - heldlock->waitTime; heldlock->holdTime = cycles; ⑵ lock->owner = current; lock->cpuid = ArchCurrCpuid(); ⑶ heldlock->lockPtr = lock; lockDep->lockDepth++; lockDep->waitLock = NULL; OsLockDepRelease(intSave); }
2.2.3 OsLockDepCheckOut(const SPIN_LOCK_S *lock) 記錄釋放自旋鎖
我們?cè)俜治鱿拢?dāng)釋放自旋鎖后,死鎖檢測(cè)特性做了哪些操作。⑴處代碼表示,當(dāng)釋放一個(gè)沒(méi)有占用的自旋鎖,會(huì)調(diào)用函數(shù)OsLockDepDumpLock()打印死鎖檢測(cè)錯(cuò)誤信息。⑵處代碼先獲取持有鎖的任務(wù)TCB的死鎖檢測(cè)變量lockDep,然后獲取其持有鎖數(shù)組的起始地址,即指針變量heldlocks。⑶獲取持有鎖的數(shù)目,然后執(zhí)行⑷,對(duì)持有的鎖進(jìn)行循環(huán)遍歷,定位到自旋鎖*lock的數(shù)組索引,再執(zhí)行⑸處代碼更新持有鎖的總時(shí)間。
⑹處代碼,判斷如果釋放的鎖,不是任務(wù)持有鎖數(shù)組的最后一個(gè),則移動(dòng)數(shù)組后面的元素,數(shù)組元素也需要減少1。最后,執(zhí)行⑺更新自旋鎖的沒(méi)有被任何CPU核、任何任務(wù)占用。
VOID OsLockDepCheckOut(SPIN_LOCK_S *lock) { UINT32 intSave; INT32 depth; VOID *requestAddr = (VOID *)__builtin_return_address(0); LosTaskCB *current = OsCurrTaskGet(); LosTaskCB *owner = NULL; LockDep *lockDep = NULL; HeldLocks *heldlocks = NULL; if (lock == NULL) { return; } OsLockDepRequire(&intSave); owner = lock->owner; ⑴ if (owner == SPINLOCK_OWNER_INIT) { OsLockDepDumpLock(current, lock, requestAddr, LOCKDEP_ERR_UNLOCK_WITHOUT_LOCK); goto OUT; } lockDep = &owner->lockDep; ⑵ heldlocks = &lockDep->heldLocks[0]; ⑶ depth = lockDep->lockDepth; while (depth-- >= 0) { ⑷ if (heldlocks[depth].lockPtr == lock) { break; } } LOS_ASSERT(depth >= 0); ⑸ heldlocks[depth].holdTime = OsLockDepGetCycles() - heldlocks[depth].holdTime; ⑹ while (depth < lockDep->lockDepth - 1) { lockDep->heldLocks[depth] = lockDep->heldLocks[depth + 1]; depth++; } lockDep->lockDepth--; ⑺ lock->cpuid = (UINT32)(-1); lock->owner = SPINLOCK_OWNER_INIT; OUT: OsLockDepRelease(intSave); }
2.2.4 OsLockdepClearSpinlocks(VOID) 釋放持有的自旋鎖
該函數(shù)OsLockdepClearSpinlocks()會(huì)全部釋放當(dāng)前任務(wù)持有的自旋鎖。在arch\arm\cortex_a_r\src\fault.c文件中,異常處理函數(shù)OsExcHandleEntry()通過(guò)調(diào)用LOCKDEP_CLEAR_LOCKS()實(shí)現(xiàn)對(duì)該函數(shù)的調(diào)用。
⑴處代碼獲取當(dāng)前任務(wù)死鎖檢測(cè)變量lockDep,然后⑵處循環(huán)變量持有的自旋鎖,獲取自旋鎖并調(diào)用LOS_SpinUnlock()進(jìn)行釋放。
VOID OsLockdepClearSpinlocks(VOID) { LosTaskCB *task = OsCurrTaskGet(); ⑴ LockDep *lockDep = &task->lockDep; SPIN_LOCK_S *lock = NULL; while (lockDep->lockDepth) { ⑵ lock = lockDep->heldLocks[lockDep->lockDepth - 1].lockPtr; LOS_SpinUnlock(lock); } }
小結(jié)
本文帶領(lǐng)大家一起剖析了SpinLock自旋鎖,LockDep死鎖檢測(cè)特性的源代碼,結(jié)合講解,參考官方示例程序代碼,自己寫(xiě)寫(xiě)程序,實(shí)際編譯運(yùn)行一下,加深理解。
感謝閱讀,如有任何問(wèn)題、建議,都可以留言給我們:?https://gitee.com/LiteOS/LiteOS/issues?。為了更容易找到LiteOS代碼倉(cāng),建議訪問(wèn)?https://gitee.com/LiteOS/LiteOS?,關(guān)注Watch、Star、并Fork到自己賬戶下,如下圖,謝謝。
匯編語(yǔ)言 輕量級(jí)操作系統(tǒng) LiteOS
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。