LiteOS內(nèi)核源碼分析系列七 互斥鎖Mutex

      網(wǎng)友投稿 648 2022-05-29

      LiteOS內(nèi)核源碼分析系列七 互斥鎖Mutex

      多任務(wù)環(huán)境下會(huì)存在多個(gè)任務(wù)訪問同一公共資源的場景,而有些公共資源是非共享的臨界資源,只能被獨(dú)占使用。LiteOS使用互斥鎖來避免這種沖突,互斥鎖是一種特殊的二值性信號(hào)量,用于實(shí)現(xiàn)對(duì)臨界資源的獨(dú)占式處理。另外,互斥鎖可以解決信號(hào)量存在的優(yōu)先級(jí)翻轉(zhuǎn)問題。用互斥鎖處理臨界資源的同步訪問時(shí),如果有任務(wù)訪問該資源,則互斥鎖為加鎖狀態(tài)。此時(shí)其他任務(wù)如果想訪問這個(gè)臨界資源則會(huì)被阻塞,直到互斥鎖被持有該鎖的任務(wù)釋放后,其他任務(wù)才能重新訪問該公共資源,此時(shí)互斥鎖再次上鎖,如此確保同一時(shí)刻只有一個(gè)任務(wù)正在訪問這個(gè)臨界資源,保證了臨界資源操作的完整性。

      本文我們來一起學(xué)習(xí)下LiteOS互斥鎖模塊的源代碼,文中所涉及的源代碼,均可以在LiteOS開源站點(diǎn)https://gitee.com/LiteOS/LiteOS 獲取。互斥鎖源代碼、開發(fā)文檔,示例程序代碼如下:

      LiteOS內(nèi)核互斥鎖源代碼

      包括互斥鎖的私有頭文件kernel\base\include\los_mux_pri.h、頭文件kernel\include\los_mux.h、C源代碼文件kernel\base\los_mux.c。

      開發(fā)指南文檔–互斥鎖

      在線文檔https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E4%BA%92%E6%96%A5%E9%94%81。

      接下來,我們看下互斥鎖的結(jié)構(gòu)體,互斥鎖初始化,互斥鎖常用操作的源代碼。

      1、互斥鎖結(jié)構(gòu)體定義和常用宏定義

      1.1 互斥鎖結(jié)構(gòu)體定義

      在文件kernel\base\include\los_mux_pri.h定義的互斥鎖控制塊結(jié)構(gòu)體有2個(gè),MuxBaseCB和LosMuxCB,前者和后者的前三個(gè)成員一樣,可以和pthread_mutex_t共享內(nèi)核互斥鎖機(jī)制。結(jié)構(gòu)體源代碼如下,結(jié)構(gòu)體成員的解釋見注釋部分。

      typedef struct { LOS_DL_LIST muxList; /**< 互斥鎖雙向鏈表 */ LosTaskCB *owner; /**< 當(dāng)前持有鎖的任務(wù) */ UINT16 muxCount; /**< 鎖被持有的次數(shù)*/ } MuxBaseCB; typedef struct { LOS_DL_LIST muxList; /**< 互斥鎖雙向鏈表 */ LosTaskCB *owner; /**< 當(dāng)前持有鎖的任務(wù) */ UINT16 muxCount; /**< 鎖被持有的次數(shù)*/ UINT8 muxStat; /**< 互斥鎖狀態(tài): OS_MUX_UNUSED, OS_MUX_USED */ UINT32 muxId; /**< 互斥鎖Id */ } LosMuxCB;

      1.2 互斥鎖常用宏定義

      系統(tǒng)支持創(chuàng)建多少互斥鎖是根據(jù)開發(fā)板情況使用宏LOSCFG_BASE_IPC_MUX_LIMIT定義的,互斥鎖Id是UINT32類型的,由2部分組成:count和muxId,分別處于高16位和低16位。創(chuàng)建互斥鎖,使用后刪除時(shí),

      互斥鎖回收到互斥鎖池時(shí),互斥鎖Id的高16位即count值會(huì)加1,這樣可以用來表示該互斥鎖被創(chuàng)建刪除的次數(shù)。muxId取值為[0,LOSCFG_BASE_IPC_MUX_LIMIT),表示互斥鎖池中各個(gè)的互斥鎖的編號(hào)。

      ⑴處的宏用來分割count和muxId的位數(shù),⑵處互斥鎖被刪除時(shí)更新互斥鎖Id,可以看出高16位為count和低16位為muxId。⑶處獲取互斥鎖Id的低16位。⑷根據(jù)互斥鎖Id獲取對(duì)應(yīng)的互斥鎖被創(chuàng)建刪除的次數(shù)count。⑸處從互斥鎖池中獲取指定互斥鎖Id對(duì)應(yīng)的互斥鎖控制塊。

      ⑴ #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))

      2、互斥鎖初始化

      互斥鎖在內(nèi)核中默認(rèn)開啟,用戶可以通過宏LOSCFG_BASE_IPC_MUX進(jìn)行關(guān)閉。開啟互斥鎖的情況下,在系統(tǒng)啟動(dòng)時(shí),在kernel\init\los_init.c中調(diào)用OsMuxInit()進(jìn)行互斥鎖模塊初始化。

      下面,我們分析下互斥鎖初始化的代碼。

      ⑴初始化雙向循環(huán)鏈表g_unusedMuxList,維護(hù)未使用的互斥鎖。⑵為互斥鎖申請內(nèi)存,如果申請失敗,則返回錯(cuò)誤LOS_ERRNO_MUX_NO_MEMORY

      ⑶循環(huán)每一個(gè)互斥鎖進(jìn)行初始化,為每一個(gè)互斥鎖節(jié)點(diǎn)指定索引muxId,owner為空,muxStat為未使用OS_MUX_UNUSED,并把互斥鎖節(jié)點(diǎn)插入未使用互斥鎖雙向鏈表g_unusedMuxList。

      ⑷如果開啟了互斥鎖調(diào)測開關(guān),則調(diào)用函數(shù)UINT32 OsMuxDbgInit(VOID)進(jìn)行初始化。

      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、互斥鎖常用操作

      3.1 互斥鎖創(chuàng)建

      我們可以使用函數(shù)UINT32 LOS_MuxCreate(UINT32 *muxHandle)來創(chuàng)建互斥鎖,下面通過分析源碼看看如何創(chuàng)建互斥鎖的。

      ⑴判斷g_unusedMuxList是否為空,還有可以使用的互斥鎖資源?如果沒有可以使用的互斥鎖,調(diào)用函數(shù)OsMutexCheckHook()判斷是否有互斥鎖溢出等錯(cuò)誤,這個(gè)函數(shù)需要開啟調(diào)測開關(guān)。

      ⑵處如果g_unusedMuxList不為空,則獲取第一個(gè)可用的互斥鎖節(jié)點(diǎn),接著從雙向鏈表g_unusedMuxList中刪除,然后調(diào)用LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList)獲取LosMuxCB *muxCreated

      ,初始化創(chuàng)建的互斥鎖信息,包含持有鎖的次數(shù)、狀態(tài)、持有者等信息。⑶初始化雙向鏈表&muxCreated->muxList,阻塞在這個(gè)互斥上的任務(wù)會(huì)掛在這個(gè)鏈表上。⑷賦值給輸出參數(shù)*muxHandle,后續(xù)程序使用這個(gè)互斥鎖Id對(duì)互斥鎖進(jìn)行其他操作。⑸開啟調(diào)測時(shí),會(huì)調(diào)用函數(shù)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 互斥鎖刪除

      我們可以使用函數(shù)LOS_MuxDelete(UINT32 muxHandle)來刪除互斥鎖,下面通過分析源碼看看如何刪除互斥鎖的。

      ⑴處判斷互斥鎖handleId是否超過LOSCFG_BASE_IPC_MUX_LIMIT,如果超過則返回錯(cuò)誤碼。⑵獲取互斥鎖控制塊LosMuxCB *muxDeleted。⑶如果要?jiǎng)h除的互斥鎖Id有問題,或者要?jiǎng)h除的互斥鎖處于未使用狀態(tài),跳轉(zhuǎn)到錯(cuò)誤標(biāo)簽進(jìn)行處理。⑷如果互斥鎖的持有者數(shù)量不為空,不允許刪除,跳轉(zhuǎn)到錯(cuò)誤標(biāo)簽進(jìn)行處理。⑸把刪除的互斥鎖回收到未使用互斥鎖雙向鏈表g_unusedMuxList,然后更新為未使用狀態(tài),更新互斥鎖Id。⑹開啟調(diào)測時(shí),會(huì)調(diào)用函數(shù)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 互斥鎖申請

      我們可以使用函數(shù)UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)來請求互斥鎖,需要的2個(gè)參數(shù)分別是互斥鎖Id和等待時(shí)間timeout,單位Tick,取值范圍為[0, LOS_WAIT_FOREVER]。

      下面通過分析源碼看看如何請求互斥鎖的。

      申請互斥鎖時(shí)首先會(huì)進(jìn)行互斥鎖Id、參數(shù)的合法性校驗(yàn),這些比較簡單。⑴處代碼判斷申請互斥鎖的是否系統(tǒng)任務(wù),如果是系統(tǒng)任務(wù)輸出警告信息。⑵如果互斥鎖沒有被持有,更新互斥鎖的持有次數(shù)和持有者信息,完成互斥鎖的申請。⑶處如果互斥鎖的持有次數(shù)不為0,并且被當(dāng)前任務(wù)持有,可以持有次數(shù)加1,再次嵌套持有,完成互斥鎖的申請。⑷如果等待時(shí)間為0,申請失敗返回。⑸如果當(dāng)前鎖任務(wù)調(diào)度,不允許申請互斥鎖,打印回溯棧并返回錯(cuò)誤碼。

      能運(yùn)行到⑹處,表示互斥鎖已被其他任務(wù)持有。在當(dāng)前申請互斥鎖的任務(wù)優(yōu)先級(jí)高于持有互斥鎖的任務(wù)優(yōu)先級(jí)時(shí),修改持有互斥鎖的優(yōu)先級(jí)為當(dāng)前任務(wù)的優(yōu)先級(jí),持有鎖的任務(wù)優(yōu)先級(jí)備份到成員變量muxPended->owner->priBitMap。通過這樣的修改,可以避免優(yōu)先級(jí)翻轉(zhuǎn)。⑺處的函數(shù)OsMuxPendOp()下文繼續(xù)分析。

      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; }

      接下來繼續(xù)分析函數(shù)OsMuxPendOp(),⑴處設(shè)置申請互斥鎖的任務(wù)的結(jié)構(gòu)體成員變量runTask->taskMux為申請的互斥鎖。⑵處獲取互斥鎖的雙向鏈表,阻塞在請求這個(gè)互斥鎖的任務(wù)都掛在這個(gè)鏈表上,后文詳細(xì)分析這個(gè)函數(shù)。⑶處把申請互斥鎖的任務(wù)改為非就緒狀態(tài)、阻塞狀態(tài),插入到互斥鎖的阻塞任務(wù)列表里。如果是非永久等待互斥鎖,還需要把任務(wù)加入超時(shí)排序鏈表里。⑷觸發(fā)任務(wù)調(diào)度,后續(xù)程序暫時(shí)不再執(zhí)行,需要等到可以獲取互斥鎖或者時(shí)間超時(shí)。

      如果時(shí)間超時(shí)或者申請到互斥鎖,系統(tǒng)重新調(diào)度到執(zhí)行此任務(wù),程序從⑸處繼續(xù)執(zhí)行。如果是時(shí)間超時(shí),⑹處更新任務(wù)狀態(tài)并返回碼,申請互斥鎖失敗。⑺如果成功申請到互斥鎖,并且超時(shí)時(shí)間不等于LOS_WAIT_FOREVER,需要判斷是否恢復(fù)任務(wù)優(yōu)先級(jí)。

      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; }

      接下來,分析下內(nèi)部函數(shù)OsMuxPendFindPos()。LiteOS互斥鎖支持2種等待模式,可以通過宏來配置:

      LOSCFG_MUTEX_WAITMODE_PRIO

      互斥鎖基于任務(wù)優(yōu)先級(jí)的等待模式,阻塞在互斥鎖的任務(wù)里,誰的優(yōu)先級(jí)高,在互斥鎖釋放時(shí),誰先獲取到互斥鎖。

      LOSCFG_MUTEX_WAITMODE_FIFO

      互斥鎖基于FIFO的等待模式,阻塞在互斥鎖的任務(wù)里,誰先進(jìn)入阻塞隊(duì)列,在互斥鎖釋放時(shí),誰先獲取到互斥鎖。

      LiteOS內(nèi)核源碼分析系列七 互斥鎖Mutex

      在開啟宏LOSCFG_MUTEX_WAITMODE_FIFO,互斥鎖基于FIFO的等待模式時(shí),函數(shù)OsMuxPendFindPos()的源碼比較簡單,直接獲取互斥鎖的阻塞鏈表,在后續(xù)的OsTaskWait()函數(shù)里,會(huì)把任務(wù)掛在在獲取的阻塞鏈表的尾部。代碼如下:

      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,互斥鎖基于任務(wù)優(yōu)先級(jí)的等待模式時(shí)的函數(shù)的代碼。⑴如果互斥鎖的阻塞鏈表為空,直接返回鏈表即可。⑵阻塞鏈表不為空時(shí),從鏈表中獲取第一個(gè)和最后一個(gè)鏈表節(jié)點(diǎn),分別為pendedTask1和pendedTask2。⑶如果阻塞鏈表第一個(gè)任務(wù)的優(yōu)先級(jí)低于當(dāng)前任務(wù)的優(yōu)先級(jí),鏈表中所有的任務(wù)的優(yōu)先級(jí)都會(huì)低,返回互斥鎖的阻塞鏈表的第一個(gè)節(jié)點(diǎn)接口。⑷如果阻塞鏈表的最后一個(gè)任務(wù)的優(yōu)先級(jí)大于當(dāng)前任務(wù)的優(yōu)先級(jí),返回互斥鎖阻塞鏈表的頭結(jié)點(diǎn)即可。⑸對(duì)于其他情況,需要調(diào)用函數(shù)OsMuxPendFindPosSub()進(jìn)行處理。

      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; }

      繼續(xù)分析下函數(shù)OsMuxPendFindPosSub()。⑴循環(huán)遍歷互斥鎖阻塞鏈表,⑵如果鏈表上任務(wù)優(yōu)先級(jí)大于當(dāng)前任務(wù)的優(yōu)先級(jí),則繼續(xù)遍歷。⑶如果鏈表上任務(wù)優(yōu)先級(jí)小于當(dāng)前任務(wù)的優(yōu)先級(jí),不需要繼續(xù)遍歷了,返回鏈表的當(dāng)前節(jié)點(diǎn)。⑷如果優(yōu)先級(jí)相等,返回鏈表的下一個(gè)節(jié)點(diǎn)。

      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 互斥鎖釋放

      我們可以使用函數(shù)UINT32 LOS_MuxPost(UINT32 muxHandle)來釋放互斥鎖,下面通過分析源碼看看如何釋放互斥鎖的。

      釋放互斥鎖時(shí)首先會(huì)進(jìn)行互斥鎖Id、參數(shù)的合法性校驗(yàn),這些比較簡單,自行閱讀即可。⑴處如果要釋放的互斥鎖沒有被持有、或者不是被當(dāng)前任務(wù)持有,返回錯(cuò)誤碼。⑵互斥鎖的持有數(shù)量減1,如果不為0,當(dāng)前任務(wù)嵌套持有該互斥鎖,不需要調(diào)度,返回釋放互斥鎖成功。如果釋放一次后,當(dāng)前任務(wù)不再持有互斥鎖,則調(diào)用⑶處函數(shù)OsMuxPostOp(),判斷是否有任務(wù)阻塞在該互斥鎖,是否需要觸發(fā)任務(wù)調(diào)度等,下文分析該函數(shù)。執(zhí)行完畢⑶后,執(zhí)行⑷,如果需要調(diào)度則觸發(fā)任務(wù)調(diào)度。

      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; }

      我們繼續(xù)分析函數(shù)OsMuxPostOp()。⑴處如果等到該互斥鎖的任務(wù)列表為空,則標(biāo)記沒有任務(wù)持有該互斥鎖,并返回不需要調(diào)度。⑵獲取等待互斥鎖的第一個(gè)任務(wù)resumedTask。如果開啟宏LOSCFG_MUTEX_WAITMODE_PRIO,如果等待互斥鎖的任務(wù)resumedTask的優(yōu)先級(jí)比當(dāng)前優(yōu)先級(jí)低,需要恢復(fù)當(dāng)前任務(wù)的優(yōu)先級(jí)。如果當(dāng)前任務(wù)優(yōu)先級(jí)runTask->priBitMap不為0,會(huì)調(diào)用⑷處的OsMuxPostOpSub函數(shù),稍后分析該函數(shù)。

      ⑸處把該互斥鎖的持有數(shù)量設(shè)置為1,持有人設(shè)置為等待互斥鎖的第一個(gè)任務(wù)resumedTask,resumedTask持有了互斥鎖不再阻塞在該互斥鎖resumedTask->taskMux = NULL。然后2個(gè)語句,屬于調(diào)測特性的。⑹處調(diào)用OsTaskWake()函數(shù),把resumedTask從互斥鎖的阻塞鏈表中刪除,從定時(shí)器排序鏈表中刪除。更新任務(wù)狀態(tài),加入就緒隊(duì)列,返回任務(wù)需要調(diào)度。

      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; }

      最后,我們分析函數(shù)OsMuxPostOpSub()。

      ⑴如果互斥鎖上還有其他任務(wù)阻塞著,獲取當(dāng)前運(yùn)行任務(wù)記錄的優(yōu)先級(jí).priBitMap。⑵處循環(huán)遍歷掛在互斥鎖阻塞鏈表上的每一個(gè)任務(wù),如果阻塞任務(wù)的優(yōu)先級(jí)不等于bitMapPri,則執(zhí)行⑶清理優(yōu)先級(jí)位。

      ⑷處恢復(fù)當(dāng)前持有互斥鎖的任務(wù)的優(yōu)先級(jí)。

      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); }

      小結(jié)

      本文帶領(lǐng)大家一起剖析了LiteOS互斥鎖模塊的源代碼,包含互斥鎖的結(jié)構(gòu)體、互斥鎖池初始化、互斥鎖創(chuàng)建刪除、申請釋放等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/LiteOS/LiteOS/issues 。為了更容易找到LiteOS代碼倉,建議訪問 https://gitee.com/LiteOS/LiteOS ,關(guān)注Watch、Star、并Fork到自己賬戶下,如下圖,謝謝。

      數(shù)據(jù)結(jié)構(gòu) 輕量級(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)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:【小白學(xué)習(xí)keras教程】一、基于波士頓住房數(shù)據(jù)集訓(xùn)練簡單的MLP回歸模型
      下一篇:Java編程02之Java編碼規(guī)范
      相關(guān)文章
      国产成人亚洲精品电影| 亚洲日本va在线观看| 亚洲va久久久久| 91情国产l精品国产亚洲区| 亚洲AV无码不卡无码| 亚洲伊人久久综合影院| 国产精品亚洲一区二区三区| 亚洲欧美日本韩国| 亚洲午夜无码久久| 亚洲精品无码一区二区| 亚洲人成色在线观看| 亚洲av色香蕉一区二区三区 | 亚洲av成人无码久久精品 | 国产亚洲视频在线| 亚洲成a人片在线观看老师| 亚洲AV网站在线观看| 精品亚洲一区二区三区在线观看 | 色拍自拍亚洲综合图区| 久久精品国产亚洲av麻豆小说| 亚洲视频国产精品| 亚洲人成影院77777| 亚洲国产日韩综合久久精品| 亚洲国产综合精品中文第一| 亚洲国产精品无码第一区二区三区| 无码亚洲成a人在线观看| 亚洲国产精品第一区二区三区| 亚洲七七久久精品中文国产| 亚洲毛片αv无线播放一区| 亚洲av福利无码无一区二区| 亚洲综合精品一二三区在线| 91亚洲精品自在在线观看| 亚洲kkk4444在线观看| 亚洲AV综合色区无码一二三区| 国产在亚洲线视频观看| 亚洲一区二区三区在线观看精品中文| 国产AV无码专区亚洲AV男同| 亚洲成人激情在线| 亚洲国产高清在线精品一区| 亚洲日韩一区二区一无码| 亚洲&#228;v永久无码精品天堂久久| 国产成人亚洲精品91专区手机|