摘要:多任務環境下會存在多個任務訪問同一公共資源的場景,而有些公共資源是非共享的臨界資源,只能被獨佔使用。LiteOS使用互斥鎖來避免這種衝突,互斥鎖是一種特殊的二值性信號量,用於實現對臨界資源的獨佔式處理。
本文分享自華爲雲社區《LiteOS內核源碼分析系列七 互斥鎖Mutex》,原文做者:zhushy。node
多任務環境下會存在多個任務訪問同一公共資源的場景,而有些公共資源是非共享的臨界資源,只能被獨佔使用。LiteOS使用互斥鎖來避免這種衝突,互斥鎖是一種特殊的二值性信號量,用於實現對臨界資源的獨佔式處理。另外,互斥鎖能夠解決信號量存在的優先級翻轉問題。用互斥鎖處理臨界資源的同步訪問時,若是有任務訪問該資源,則互斥鎖爲加鎖狀態。此時其餘任務若是想訪問這個臨界資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其餘任務才能從新訪問該公共資源,此時互斥鎖再次上鎖,如此確保同一時刻只有一個任務正在訪問這個臨界資源,保證了臨界資源操做的完整性。git
本文咱們來一塊兒學習下LiteOS互斥鎖模塊的源代碼,文中所涉及的源代碼, 都可以在LiteOS
開源站點https://gitee.com/LiteOS/LiteOS 獲取。互斥鎖源代碼、開發文檔,示例程序代碼以下:ide
- LiteOS內核互斥鎖源代碼
包括互斥鎖的私有頭文件kernel\base\include\los_mux_pri.h、頭文件kernel\include\los_mux.h、C源代碼文件kernel\base\los_mux.c。函數
- 開發指南文檔–互斥鎖
在線文檔https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E4%BA%92%E6%96%A5%E9%94%81。源碼分析
接下來,咱們看下互斥鎖的結構體,互斥鎖初始化,互斥鎖經常使用操做的源代碼。學習
一、互斥鎖結構體定義和經常使用宏定義
1.1 互斥鎖結構體定義
在文件kernel\base\include\los_mux_pri.h定義的互斥鎖控制塊結構體有2個,MuxBaseCB和LosMuxCB,前者和後者的前三個成員同樣,能夠和pthread_mutex_t共享內核互斥鎖機制。結構體源代碼以下,結構體成員的解釋見註釋部分。ui
typedef struct { LOS_DL_LIST muxList; /**< 互斥鎖雙向鏈表 */ LosTaskCB *owner; /**< 當前持有鎖的任務 */ UINT16 muxCount; /**< 鎖被持有的次數*/ } MuxBaseCB; typedef struct { LOS_DL_LIST muxList; /**< 互斥鎖雙向鏈表 */ LosTaskCB *owner; /**< 當前持有鎖的任務 */ UINT16 muxCount; /**< 鎖被持有的次數*/ UINT8 muxStat; /**< 互斥鎖狀態: OS_MUX_UNUSED, OS_MUX_USED */ UINT32 muxId; /**< 互斥鎖Id */ } LosMuxCB;
1.2 互斥鎖經常使用宏定義
系統支持建立多少互斥鎖是根據開發板狀況使用宏LOSCFG_BASE_IPC_MUX_LIMIT定義的,互斥鎖Id是UINT32類型的,由2部分組成:count和muxId,分別處於高16位和低16位。建立互斥鎖,使用後刪除時,互斥鎖回收到互斥鎖池時,互斥鎖Id的高16位即count值會加1,這樣能夠用來表示該互斥鎖被建立刪除的次數。muxId取值爲[0,LOSCFG_BASE_IPC_MUX_LIMIT),表示互斥鎖池中各個的互斥鎖的編號。url
⑴處的宏用來分割count和muxId的位數,⑵處互斥鎖被刪除時更新互斥鎖Id,能夠看出高16位爲count和低16位爲muxId。⑶處獲取互斥鎖Id的低16位。⑷根據互斥鎖Id獲取對應的互斥鎖被建立刪除的次數count。⑸處從互斥鎖池中獲取指定互斥鎖Id對應的互斥鎖控制塊。spa
⑴ #define MUX_SPLIT_BIT 16 ⑵ #define SET_MUX_ID(count, muxId) (((count) << MUX_SPLIT_BIT) | (muxId)) ⑶ #define GET_MUX_INDEX(muxId) ((muxId) & ((1U << MUX_SPLIT_BIT) - 1)) ⑷ #define GET_MUX_COUNT(muxId) ((muxId) >> MUX_SPLIT_BIT) ⑸ #define GET_MUX(muxId) (((LosMuxCB *)g_allMux) + GET_MUX_INDEX(muxId))
二、互斥鎖初始化
互斥鎖在內核中默認開啓,用戶能夠經過宏LOSCFG_BASE_IPC_MUX進行關閉。開啓互斥鎖的狀況下,在系統啓動時,在kernel\init\los_init.c中調用OsMuxInit()進行互斥鎖模塊初始化。.net
下面,咱們分析下互斥鎖初始化的代碼。
⑴初始化雙向循環鏈表g_unusedMuxList,維護未使用的互斥鎖。⑵爲互斥鎖申請內存,若是申請失敗,則返回錯誤LOS_ERRNO_MUX_NO_MEMORY
⑶循環每個互斥鎖進行初始化,爲每個互斥鎖節點指定索引muxId,owner爲空,muxStat爲未使用OS_MUX_UNUSED,並把互斥鎖節點插入未使用互斥鎖雙向鏈表g_unusedMuxList。
⑷若是開啓了互斥鎖調測開關,則調用函數UINT32 OsMuxDbgInit(VOID)進行初始化。
LITE_OS_SEC_TEXT UINT32 OsMuxInit(VOID) { LosMuxCB *muxNode = NULL; UINT32 index; ⑴ LOS_ListInit(&g_unusedMuxList); ⑵ g_allMux = (LosMuxCB *)LOS_MemAlloc(m_aucSysMem0, (LOSCFG_BASE_IPC_MUX_LIMIT * sizeof(LosMuxCB))); if (g_allMux == NULL) { return LOS_ERRNO_MUX_NO_MEMORY; } ⑶ for (index = 0; index < LOSCFG_BASE_IPC_MUX_LIMIT; index++) { muxNode = g_allMux + index; muxNode->muxId = index; muxNode->owner = NULL; muxNode->muxStat = OS_MUX_UNUSED; LOS_ListTailInsert(&g_unusedMuxList, &muxNode->muxList); } ⑷ if (OsMuxDbgInitHook() != LOS_OK) { return LOS_ERRNO_MUX_NO_MEMORY; } return LOS_OK; }
三、互斥鎖經常使用操做
3.1 互斥鎖建立
咱們可使用函數UINT32 LOS_MuxCreate(UINT32 *muxHandle)來建立互斥鎖,下面經過分析源碼看看如何建立互斥鎖的。
⑴判斷g_unusedMuxList是否爲空,還有可使用的互斥鎖資源?若是沒有可使用的互斥鎖,調用函數OsMutexCheckHook()判斷是否有互斥鎖溢出等錯誤,這個函數須要開啓調測開關。⑵處若是g_unusedMuxList不爲空,則獲取第一個可用的互斥鎖節點,接着從雙向鏈表g_unusedMuxList中刪除,而後調用LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList)獲取LosMuxCB *muxCreated,初始化建立的互斥鎖信息,包含持有鎖的次數、狀態、持有者等信息。⑶初始化雙向鏈表&muxCreated->muxList,阻塞在這個互斥上的任務會掛在這個鏈表上。⑷賦值給輸出參數*muxHandle,後續程序使用這個互斥鎖Id對互斥鎖進行其餘操做。⑸開啓調測時,會調用函數OsMuxDbgUpdateHook()更新互斥鎖的使用狀況。
LITE_OS_SEC_TEXT UINT32 LOS_MuxCreate(UINT32 *muxHandle) { UINT32 intSave; LosMuxCB *muxCreated = NULL; LOS_DL_LIST *unusedMux = NULL; UINT32 errNo; UINT32 errLine; if (muxHandle == NULL) { return LOS_ERRNO_MUX_PTR_NULL; } SCHEDULER_LOCK(intSave); ⑴ if (LOS_ListEmpty(&g_unusedMuxList)) { SCHEDULER_UNLOCK(intSave); OsMutexCheckHook(); OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_ALL_BUSY); } ⑵ unusedMux = LOS_DL_LIST_FIRST(&g_unusedMuxList); LOS_ListDelete(unusedMux); muxCreated = LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList); muxCreated->muxCount = 0; muxCreated->muxStat = OS_MUX_USED; muxCreated->owner = NULL; ⑶ LOS_ListInit(&muxCreated->muxList); *muxHandle = muxCreated->muxId; ⑸ OsMuxDbgUpdateHook(muxCreated->muxId, OsCurrTaskGet()->taskEntry); SCHEDULER_UNLOCK(intSave); LOS_TRACE(MUX_CREATE, muxCreated->muxId); return LOS_OK; ERR_HANDLER: OS_RETURN_ERROR_P2(errLine, errNo); }
3.2 互斥鎖刪除
咱們可使用函數LOS_MuxDelete(UINT32 muxHandle)來刪除互斥鎖,下面經過分析源碼看看如何刪除互斥鎖的。
⑴處判斷互斥鎖handleId是否超過LOSCFG_BASE_IPC_MUX_LIMIT,若是超過則返回錯誤碼。⑵獲取互斥鎖控制塊LosMuxCB *muxDeleted。⑶若是要刪除的互斥鎖Id有問題,或者要刪除的互斥鎖處於未使用狀態,跳轉到錯誤標籤進行處理。⑷若是互斥鎖的持有者數量不爲空,不容許刪除,跳轉到錯誤標籤進行處理。⑸把刪除的互斥鎖回收到未使用互斥鎖雙向鏈表g_unusedMuxList,而後更新爲未使用狀態,更新互斥鎖Id。⑹開啓調測時,會調用函數OsMuxDbgUpdateHook()更新互斥鎖的使用狀況。
LITE_OS_SEC_TEXT UINT32 LOS_MuxDelete(UINT32 muxHandle) { UINT32 intSave; LosMuxCB *muxDeleted = NULL; UINT32 errNo; UINT32 errLine; ⑴ if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) { OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID); } ⑵ muxDeleted = GET_MUX(muxHandle); LOS_TRACE(MUX_DELETE, muxHandle, muxDeleted->muxStat, muxDeleted->muxCount, ((muxDeleted->owner == NULL) ? 0xFFFFFFFF : muxDeleted->owner->taskId)); SCHEDULER_LOCK(intSave); ⑶ if ((muxDeleted->muxId != muxHandle) || (muxDeleted->muxStat == OS_MUX_UNUSED)) { SCHEDULER_UNLOCK(intSave); OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_INVALID); } ⑷ if (!LOS_ListEmpty(&muxDeleted->muxList) || muxDeleted->muxCount) { SCHEDULER_UNLOCK(intSave); OS_GOTO_ERR_HANDLER(LOS_ERRNO_MUX_PENDED); } ⑸ LOS_ListTailInsert(&g_unusedMuxList, &muxDeleted->muxList); muxDeleted->muxStat = OS_MUX_UNUSED; muxDeleted->muxId = SET_MUX_ID(GET_MUX_COUNT(muxDeleted->muxId) + 1, GET_MUX_INDEX(muxDeleted->muxId)); ⑹ OsMuxDbgUpdateHook(muxDeleted->muxId, NULL); SCHEDULER_UNLOCK(intSave); return LOS_OK; ERR_HANDLER: OS_RETURN_ERROR_P2(errLine, errNo); }
3.3 互斥鎖申請
咱們可使用函數UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)來請求互斥鎖,須要的2個參數分別是互斥鎖Id和等待時間timeout,單位Tick,取值範圍爲[0, LOS_WAIT_FOREVER]。
下面經過分析源碼看看如何請求互斥鎖的。
申請互斥鎖時首先會進行互斥鎖Id、參數的合法性校驗,這些比較簡單。⑴處代碼判斷申請互斥鎖的是否系統任務,若是是系統任務輸出警告信息。⑵若是互斥鎖沒有被持有,更新互斥鎖的持有次數和持有者信息,完成互斥鎖的申請。⑶處若是互斥鎖的持有次數不爲0,而且被當前任務持有,能夠持有次數加1,再次嵌套持有,完成互斥鎖的申請。⑷若是等待時間爲0,申請失敗返回。⑸若是當前鎖任務調度,不容許申請互斥鎖,打印回溯棧並返回錯誤碼。
能運行到⑹處,表示互斥鎖已被其餘任務持有。在當前申請互斥鎖的任務優先級高於持有互斥鎖的任務優先級時,修改持有互斥鎖的優先級爲當前任務的優先級,持有鎖的任務優先級備份到成員變量muxPended->owner->priBitMap。經過這樣的修改,能夠避免優先級翻轉。⑺處的函數OsMuxPendOp()下文繼續分析。
LITE_OS_SEC_TEXT UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout) { UINT32 ret; UINT32 intSave; LosMuxCB *muxPended = NULL; LosTaskCB *runTask = NULL; if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) { OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID); } muxPended = GET_MUX(muxHandle); LOS_TRACE(MUX_PEND, muxHandle, muxPended->muxCount, ((muxPended->owner == NULL) ? 0xFFFFFFFF : muxPended->owner->taskId), timeout); SCHEDULER_LOCK(intSave); ret = OsMuxParaCheck(muxPended, muxHandle); if (ret != LOS_OK) { goto OUT_UNLOCK; } runTask = OsCurrTaskGet(); ⑴ if (runTask->taskFlags & OS_TASK_FLAG_SYSTEM) { PRINT_DEBUG("Warning: DO NOT recommend to use %s in system tasks.\n", __FUNCTION__); } ⑵ if (muxPended->muxCount == 0) { OsMuxDlockNodeInsertHook(runTask->taskId, muxPended); muxPended->muxCount++; muxPended->owner = runTask; goto OUT_UNLOCK; } ⑶ if (muxPended->owner == runTask) { muxPended->muxCount++; goto OUT_UNLOCK; } ⑷ if (!timeout) { ret = LOS_ERRNO_MUX_UNAVAILABLE; goto OUT_UNLOCK; } ⑸ if (!OsPreemptableInSched()) { ret = LOS_ERRNO_MUX_PEND_IN_LOCK; PRINT_ERR("!!!LOS_ERRNO_MUX_PEND_IN_LOCK!!!\n"); OsBackTrace(); goto OUT_UNLOCK; } ⑹ OsMuxBitmapSet(runTask, (MuxBaseCB *)muxPended); ⑺ ret = OsMuxPendOp(runTask, (MuxBaseCB *)muxPended, timeout, &intSave); OUT_UNLOCK: SCHEDULER_UNLOCK(intSave); return ret; }
接下來繼續分析函數OsMuxPendOp(),⑴處設置申請互斥鎖的任務的結構體成員變量runTask->taskMux爲申請的互斥鎖。⑵處獲取互斥鎖的雙向鏈表,阻塞在請求這個互斥鎖的任務都掛在這個鏈表上,後文詳細分析這個函數。⑶處把申請互斥鎖的任務改成非就緒狀態、阻塞狀態,插入到互斥鎖的阻塞任務列表裏。若是是非永久等待互斥鎖,還須要把任務加入超時排序鏈表裏。⑷觸發任務調度,後續程序暫時再也不執行,須要等到能夠獲取互斥鎖或者時間超時。
若是時間超時或者申請到互斥鎖,系統從新調度到執行此任務,程序從⑸處繼續執行。若是是時間超時,⑹處更新任務狀態並返回碼,申請互斥鎖失敗。⑺若是成功申請到互斥鎖,而且超時時間不等於LOS_WAIT_FOREVER,須要判斷是否恢復任務優先級。
LITE_OS_SEC_TEXT UINT32 OsMuxPendOp(LosTaskCB *runTask, MuxBaseCB *muxPended, UINT32 timeout, UINT32 *intSave) { LOS_DL_LIST *node = NULL; UINT32 ret = LOS_OK; LosTaskCB *owner = muxPended->owner; ⑴ runTask->taskMux = (VOID *)muxPended; ⑵ node = OsMuxPendFindPos(runTask, muxPended); ⑶ OsTaskWait(node, OS_TASK_STATUS_PEND, timeout); ⑷ OsSchedResched(); SCHEDULER_UNLOCK(*intSave); ⑸ SCHEDULER_LOCK(*intSave); ⑹ if (runTask->taskStatus & OS_TASK_STATUS_TIMEOUT) { runTask->taskStatus &= ~OS_TASK_STATUS_TIMEOUT; ret = LOS_ERRNO_MUX_TIMEOUT; } ⑺ if (timeout != LOS_WAIT_FOREVER) { OsMuxBitmapRestore(runTask, owner); } return ret; }
接下來,分析下內部函數OsMuxPendFindPos()。LiteOS互斥鎖支持2種等待模式,能夠經過宏來配置:
- LOSCFG_MUTEX_WAITMODE_PRIO
互斥鎖基於任務優先級的等待模式,阻塞在互斥鎖的任務裏,誰的優先級高,在互斥鎖釋放時,誰先獲取到互斥鎖。
- LOSCFG_MUTEX_WAITMODE_FIFO
互斥鎖基於FIFO的等待模式,阻塞在互斥鎖的任務裏,誰先進入阻塞隊列,在互斥鎖釋放時,誰先獲取到互斥鎖。
在開啓宏LOSCFG_MUTEX_WAITMODE_FIFO,互斥鎖基於FIFO的等待模式時,函數OsMuxPendFindPos()的源碼比較簡單,直接獲取互斥鎖的阻塞鏈表,在後續的OsTaskWait()函數裏,會把任務掛在在獲取的阻塞鏈表的尾部。代碼以下:
LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPos(const LosTaskCB *runTask, MuxBaseCB *muxPended) { LOS_DL_LIST *node = NULL; node = &muxPended->muxList; return node; }
咱們再來看看開啓宏LOSCFG_MUTEX_WAITMODE_PRIO,互斥鎖基於任務優先級的等待模式時的函數的代碼。⑴若是互斥鎖的阻塞鏈表爲空,直接返回鏈表便可。⑵阻塞鏈表不爲空時,從鏈表中獲取第一個和最後一個鏈表節點,分別爲pendedTask1和pendedTask2。⑶若是阻塞鏈表第一個任務的優先級低於當前任務的優先級,鏈表中全部的任務的優先級都會低,返回互斥鎖的阻塞鏈表的第一個節點接口。⑷若是阻塞鏈表的最後一個任務的優先級大於當前任務的優先級,返回互斥鎖阻塞鏈表的頭結點便可。⑸對於其餘狀況,須要調用函數OsMuxPendFindPosSub()進行處理。
LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPos(const LosTaskCB *runTask, MuxBaseCB *muxPended) { LOS_DL_LIST *node = NULL; LosTaskCB *pendedTask1 = NULL; LosTaskCB *pendedTask2 = NULL; ⑴ if (LOS_ListEmpty(&muxPended->muxList)) { node = &muxPended->muxList; } else { ⑵ pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&muxPended->muxList)); pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(&muxPended->muxList)); ⑶ if ((pendedTask1 != NULL) && (pendedTask1->priority > runTask->priority)) { node = muxPended->muxList.pstNext; ⑷ } else if ((pendedTask2 != NULL) && (pendedTask2->priority <= runTask->priority)) { node = &muxPended->muxList; } else { ⑸ node = OsMuxPendFindPosSub(runTask, muxPended); } } return node; }
繼續分析下函數OsMuxPendFindPosSub()。⑴循環遍歷互斥鎖阻塞鏈表,⑵若是鏈表上任務優先級大於當前任務的優先級,則繼續遍歷。⑶若是鏈表上任務優先級小於當前任務的優先級,不須要繼續遍歷了,返回鏈表的當前節點。⑷若是優先級相等,返回鏈表的下一個節點。
LITE_OS_SEC_TEXT STATIC LOS_DL_LIST *OsMuxPendFindPosSub(const LosTaskCB *runTask, const MuxBaseCB *muxPended) { LosTaskCB *pendedTask = NULL; LOS_DL_LIST *node = NULL; ⑴ LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, &(muxPended->muxList), LosTaskCB, pendList) { ⑵ if (pendedTask->priority < runTask->priority) { continue; ⑶ } else if (pendedTask->priority > runTask->priority) { node = &pendedTask->pendList; break; } else { ⑷ node = pendedTask->pendList.pstNext; break; } } return node; }
3.4 互斥鎖釋放
咱們可使用函數UINT32 LOS_MuxPost(UINT32 muxHandle)來釋放互斥鎖,下面經過分析源碼看看如何釋放互斥鎖的。
釋放互斥鎖時首先會進行互斥鎖Id、參數的合法性校驗,這些比較簡單,自行閱讀便可。⑴處若是要釋放的互斥鎖沒有被持有、或者不是被當前任務持有,返回錯誤碼。⑵互斥鎖的持有數量減1,若是不爲0,當前任務嵌套持有該互斥鎖,不須要調度,返回釋放互斥鎖成功。若是釋放一次後,當前任務再也不持有互斥鎖,則調用⑶處函數OsMuxPostOp(),判斷是否有任務阻塞在該互斥鎖,是否須要觸發任務調度等,下文分析該函數。執行完畢⑶後,執行⑷,若是須要調度則觸發任務調度。
LITE_OS_SEC_TEXT UINT32 LOS_MuxPost(UINT32 muxHandle) { UINT32 ret; LosTaskCB *runTask = NULL; LosMuxCB *muxPosted = GET_MUX(muxHandle); UINT32 intSave; if (GET_MUX_INDEX(muxHandle) >= (UINT32)LOSCFG_BASE_IPC_MUX_LIMIT) { OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID); } LOS_TRACE(MUX_POST, muxHandle, muxPosted->muxCount, ((muxPosted->owner == NULL) ? 0xFFFFFFFF : muxPosted->owner->taskId)); SCHEDULER_LOCK(intSave); ret = OsMuxParaCheck(muxPosted, muxHandle); if (ret != LOS_OK) { SCHEDULER_UNLOCK(intSave); return ret; } runTask = OsCurrTaskGet(); ⑴ if ((muxPosted->muxCount == 0) || (muxPosted->owner != runTask)) { SCHEDULER_UNLOCK(intSave); OS_RETURN_ERROR(LOS_ERRNO_MUX_INVALID); } ⑵ if (--muxPosted->muxCount != 0) { SCHEDULER_UNLOCK(intSave); return LOS_OK; } ⑶ ret = OsMuxPostOp(runTask, (MuxBaseCB *)muxPosted); SCHEDULER_UNLOCK(intSave); ⑷ if (ret == MUX_SCHEDULE) { LOS_MpSchedule(OS_MP_CPU_ALL); LOS_Schedule(); } return LOS_OK; }
咱們繼續分析函數OsMuxPostOp()。⑴處若是等到該互斥鎖的任務列表爲空,則標記沒有任務持有該互斥鎖,並返回不須要調度。⑵獲取等待互斥鎖的第一個任務resumedTask。若是開啓宏LOSCFG_MUTEX_WAITMODE_PRIO,若是等待互斥鎖的任務resumedTask的優先級比當前優先級低,須要恢復當前任務的優先級。若是當前任務優先級runTask->priBitMap不爲0,會調用⑷處的OsMuxPostOpSub函數,稍後分析該函數。
⑸處把該互斥鎖的持有數量設置爲1,持有人設置爲等待互斥鎖的第一個任務resumedTask,resumedTask持有了互斥鎖再也不阻塞在該互斥鎖resumedTask->taskMux = NULL。而後2個語句,屬於調測特性的。⑹處調用OsTaskWake()函數,把resumedTask從互斥鎖的阻塞鏈表中刪除,從定時器排序鏈表中刪除。更新任務狀態,加入就緒隊列,返回任務須要調度。
LITE_OS_SEC_TEXT UINT32 OsMuxPostOp(LosTaskCB *runTask, MuxBaseCB *muxPosted) { LosTaskCB *resumedTask = NULL; ⑴ if (LOS_ListEmpty(&muxPosted->muxList)) { muxPosted->owner = NULL; OsMuxDlockNodeDeleteHook(runTask->taskId, muxPosted); return MUX_NO_SCHEDULE; } ⑵ resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(muxPosted->muxList))); #ifdef LOSCFG_MUTEX_WAITMODE_PRIO ⑶ if (resumedTask->priority > runTask->priority) { if (LOS_HighBitGet(runTask->priBitMap) != resumedTask->priority) { LOS_BitmapClr(&runTask->priBitMap, resumedTask->priority); } } else if (runTask->priBitMap != 0) { ⑷ OsMuxPostOpSub(runTask, muxPosted); } #else if (runTask->priBitMap != 0) { ⑷ OsMuxPostOpSub(runTask, muxPosted); } #endif ⑸ muxPosted->muxCount = 1; muxPosted->owner = resumedTask; resumedTask->taskMux = NULL; OsMuxDlockNodeDeleteHook(runTask->taskId, muxPosted); OsMuxDlockNodeInsertHook(resumedTask->taskId, muxPosted); ⑹ OsTaskWake(resumedTask, OS_TASK_STATUS_PEND); return MUX_SCHEDULE; }
最後,咱們分析函數OsMuxPostOpSub()。
⑴若是互斥鎖上還有其餘任務阻塞着,獲取當前運行任務記錄的優先級.priBitMap。⑵處循環遍歷掛在互斥鎖阻塞鏈表上的每個任務,若是阻塞任務的優先級不等於bitMapPri,則執行⑶清理優先級位。⑷處恢復當前持有互斥鎖的任務的優先級。
LITE_OS_SEC_TEXT STATIC VOID OsMuxPostOpSub(LosTaskCB *runTask, MuxBaseCB *muxPosted) { LosTaskCB *pendedTask = NULL; UINT16 bitMapPri; ⑴ if (!LOS_ListEmpty(&muxPosted->muxList)) { bitMapPri = LOS_HighBitGet(runTask->priBitMap); ⑵ LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, (&muxPosted->muxList), LosTaskCB, pendList) { if (bitMapPri != pendedTask->priority) { ⑶ LOS_BitmapClr(&runTask->priBitMap, pendedTask->priority); } } } ⑷ bitMapPri = LOS_LowBitGet(runTask->priBitMap); LOS_BitmapClr(&runTask->priBitMap, bitMapPri); OsTaskPriModify(muxPosted->owner, bitMapPri); }
小結
本文帶領你們一塊兒剖析了LiteOS互斥鎖模塊的源代碼,包含互斥鎖的結構體、互斥鎖池初始化、互斥鎖建立刪除、申請釋放等。