摘要:多任務環境下會存在多個任務通路同一公共資源的場景,而有些公共資源是非共享的臨界資源,隻能被獨占使用。LiteOS使用互斥鎖來避免這種沖突,互斥鎖是一種特殊的二值性信号量,用于實作對臨界資源的獨占式處理。
多任務環境下會存在多個任務通路同一公共資源的場景,而有些公共資源是非共享的臨界資源,隻能被獨占使用。LiteOS使用互斥鎖來避免這種沖突,互斥鎖是一種特殊的二值性信号量,用于實作對臨界資源的獨占式處理。另外,互斥鎖可以解決信号量存在的優先級翻轉問題。用互斥鎖處理臨界資源的同步通路時,如果有任務通路該資源,則互斥鎖為加鎖狀态。此時其他任務如果想通路這個臨界資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其他任務才能重新通路該公共資源,此時互斥鎖再次上鎖,如此確定同一時刻隻有一個任務正在通路這個臨界資源,保證了臨界資源操作的完整性。
本文我們來一起學習下LiteOS互斥鎖子產品的源代碼,文中所涉及的源代碼,均可以在
LiteOS
開源站點https://gitee.com/LiteOS/LiteOS 擷取。互斥鎖源代碼、開發文檔,示例程式代碼如下:
- 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.1 互斥鎖結構體定義
在檔案kernel\base\include\los_mux_pri.h定義的互斥鎖控制塊結構體有2個,MuxBaseCB和LosMuxCB,前者和後者的前三個成員一樣,可以和pthread_mutex_t共享核心互斥鎖機制。結構體源代碼如下,結構體成員的解釋見注釋部分。
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),表示互斥鎖池中各個的互斥鎖的編号。
⑴處的宏用來分割count和muxId的位數,⑵處互斥鎖被删除時更新互斥鎖Id,可以看出高16位為count和低16位為muxId。⑶處擷取互斥鎖Id的低16位。⑷根據互斥鎖Id擷取對應的互斥鎖被建立删除的次數count。⑸處從互斥鎖池中擷取指定互斥鎖Id對應的互斥鎖控制塊。
⑴ #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、互斥鎖初始化
互斥鎖在核心中預設開啟,使用者可以通過宏LOSCFG_BASE_IPC_MUX進行關閉。開啟互斥鎖的情況下,在系統啟動時,在kernel\init\los_init.c中調用OsMuxInit()進行互斥鎖子產品初始化。
下面,我們分析下互斥鎖初始化的代碼。
⑴初始化雙向循環連結清單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、互斥鎖常用操作
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互斥鎖子產品的源代碼,包含互斥鎖的結構體、互斥鎖池初始化、互斥鎖建立删除、申請釋放等。
本文分享自華為雲社群《LiteOS核心源碼分析系列七 互斥鎖Mutex》,原文作者:zhushy。
點選關注,第一時間了解華為雲新鮮技術~