摘要:本文帶領大家一起剖析鴻蒙輕核心的互斥鎖子產品的源代碼,包含互斥鎖的結構體、互斥鎖池初始化、互斥鎖建立删除、申請釋放等。
本文分享自華為雲社群《鴻蒙輕核心M核源碼分析系列十 互斥鎖Mutex》,原文作者:zhushy 。
多任務環境下會存在多個任務通路同一公共資源的場景,而有些公共資源是非共享的臨界資源,隻能被獨占使用。鴻蒙輕核心使用互斥鎖來避免這種沖突,互斥鎖是一種特殊的二值性信号量,用于實作對臨界資源的獨占式處理。另外,互斥鎖可以解決信号量存在的優先級翻轉問題。用互斥鎖處理臨界資源的同步通路時,如果有任務通路該資源,則互斥鎖為加鎖狀态。此時其他任務如果想通路這個臨界資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其他任務才能重新通路該公共資源,此時互斥鎖再次上鎖,如此確定同一時刻隻有一個任務正在通路這個臨界資源,保證了臨界資源操作的完整性。
本文我們來一起學習下鴻蒙輕核心互斥鎖子產品的源代碼,本文中所涉及的源碼,以OpenHarmony LiteOS-M核心為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 擷取。
接下來,我們看下互斥鎖的結構體,互斥鎖初始化,互斥鎖常用操作的源代碼。
在檔案kernel\include\los_mux.h定義的互斥鎖控制塊結構體LosMuxCB,源代碼如下,結構體成員的解釋見注釋部分。
系統支援建立多少互斥鎖是根據開發闆情況使用宏LOSCFG_BASE_IPC_MUX_LIMIT定義的,互斥鎖muxId是UINT32類型的,muxId取值為[0,LOSCFG_BASE_IPC_MUX_LIMIT),表示互斥鎖池中各個的互斥鎖的編号。
⑴處、⑵處的宏表示互斥鎖的未使用、使用狀态值。⑶處從互斥鎖池中擷取指定互斥鎖muxid對應的互斥鎖控制塊。⑷處根據互斥鎖雙向連結清單中的連結清單節點指針ptr擷取互斥鎖控制塊結構體指針。
互斥鎖在核心中預設開啟,使用者可以通過宏LOSCFG_BASE_IPC_MUX進行關閉。開啟互斥鎖的情況下,在系統啟動時,在kernel\src\los_init.c中調用OsMuxInit()進行互斥鎖子產品初始化。
下面,我們分析下互斥鎖初始化的代碼。
⑴初始化雙向循環連結清單g_unusedMuxList,維護未使用的互斥鎖。⑵處如果沒有設定宏LOSCFG_BASE_IPC_MUX,則傳回錯誤碼。⑶為互斥鎖申請記憶體,如果申請失敗,則傳回錯誤LOS_ERRNO_MUX_NO_MEMORY
⑷循環每一個互斥鎖進行初始化,為每一個互斥鎖節點指定索引muxID,muxStat為未使用OS_MUX_UNUSED,并把互斥鎖節點插入未使用互斥鎖雙向連結清單g_unusedMuxList。
⑷如果開啟了互斥鎖調測開關,則調用函數UINT32 OsMuxDbgInit(VOID)進行初始化。
我們可以使用函數UINT32 LOS_MuxCreate(UINT32 *muxHandle)來建立互斥鎖,下面通過分析源碼看看如何建立互斥鎖的。
⑴判斷未使用互斥鎖連結清單g_unusedMuxList是否為空,如果沒有可以使用的互斥鎖,跳轉到錯誤碼。⑵處如果g_unusedMuxList不為空,則擷取第一個可用的互斥鎖節點,接着從雙向連結清單g_unusedMuxList中删除,然後調用GET_MUX_LIST宏函數擷取LosMuxCB *muxCreated,接着初始化建立的互斥鎖資訊,包含持有鎖的次數、狀态、優先級等資訊。⑶初始化雙向連結清單&muxCreated->muxList,阻塞在這個互斥鎖上的任務會挂在這個連結清單上。⑷指派給輸出參數*muxHandle,後續程式使用這個互斥鎖Id對互斥鎖進行其他操作。
我們可以使用函數LOS_MuxDelete(UINT32 muxHandle)來删除互斥鎖,下面通過分析源碼看看如何删除互斥鎖的。
⑴處判斷互斥鎖muxHandle是否超過LOSCFG_BASE_IPC_MUX_LIMIT,如果超過則傳回錯誤碼。⑵擷取互斥鎖控制塊LosMuxCB *muxDeleted。⑶如果要删除的互斥鎖處于未使用狀态,跳轉到錯誤标簽進行處理。⑷如果互斥鎖的持有者數量不為空,不允許删除,跳轉到錯誤标簽進行處理。⑸把删除的互斥鎖回收到未使用互斥鎖雙向連結清單g_unusedMuxList,然後更新為未使用狀态。
我們可以使用函數UINT32 LOS_MuxPend(UINT32 muxHandle, UINT32 timeout)來請求互斥鎖,需要的2個參數分别是互斥鎖Id和等待時間timeout,機關Tick,取值範圍為[0, LOS_WAIT_FOREVER]。
下面通過分析源碼看看如何請求互斥鎖的。
申請互斥鎖時首先會進行互斥鎖Id、參數的合法性校驗,這些比較簡單。⑴處代碼擷取目前運作的任務,⑵如果互斥鎖沒有被持有,更新互斥鎖的持有次數、持有者資訊和優先級,完成互斥鎖的申請。⑶處如果互斥鎖的持有次數不為0,并且被目前任務持有,可以持有次數加1,再次嵌套持有,完成互斥鎖的申請。如果代碼執行到⑷,說明申請的互斥鎖被其他任務持有着,此時如果等待時間為0,則申請失敗傳回。⑸處更新目前任務阻塞在申請的互斥鎖上。
⑹處代碼表示在目前申請互斥鎖的任務優先級高于持有互斥鎖的任務優先級時,修改持有互斥鎖的優先級為目前任務的優先級。通過這樣的修改,可以避免優先級翻轉。⑺處調用函數OsSchedTaskWait()更新目前任務的狀态,設定等待時間,然後調用函數LOS_Schedule觸發任務排程。後續程式暫時不再執行,需要等到可以擷取互斥鎖或者時間逾時。
如果時間逾時或者申請到互斥鎖,系統重新排程到執行此任務,程式從⑻處繼續執行。如果是時間逾時,⑼處更新任務狀态并傳回碼,申請互斥鎖失敗。如果成功申請到互斥鎖,執行⑽,傳回成功。
我們可以使用函數UINT32 LOS_MuxPost(UINT32 muxHandle)來釋放互斥鎖,下面通過分析源碼看看如何釋放互斥鎖的。
釋放互斥鎖時首先會進行互斥鎖Id、參數的合法性校驗,這些比較簡單,自行閱讀即可。⑴處如果要釋放的互斥鎖沒有被持有、或者不是被目前任務持有,傳回錯誤碼。⑵互斥鎖的持有數量減1,如果不為0,目前任務嵌套持有該互斥鎖,不需要排程,傳回釋放互斥鎖成功。如果釋放一次後,目前任務不再持有互斥鎖,則執行⑶,如果持有互斥鎖任務的優先級不等于互斥鎖的備份優先級低,需要恢複目前任務的優先級。
⑷如果互斥鎖上還有其他任務阻塞着,擷取阻塞的任務resumedTask,該任務成功擷取到互斥鎖,然後執行⑸更新互斥鎖的持有資訊。執行⑹更新任務resumedTask的狀态,然後調用函數LOS_Schedule觸發排程。
本文帶領大家一起剖析了鴻蒙輕核心的互斥鎖子產品的源代碼,包含互斥鎖的結構體、互斥鎖池初始化、互斥鎖建立删除、申請釋放等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/openharmony/kernel_liteos_m/issues 。為了更容易找到鴻蒙輕核心代碼倉,建議通路 https://gitee.com/openharmony/kernel_liteos_m ,關注<code>Watch</code>、點贊<code>Star</code>、并<code>Fork</code>到自己賬戶下,謝謝。
點選關注,第一時間了解華為雲新鮮技術~