1. 概述
1.1 基本概念
互斥鎖又稱互斥型訊號量,是一種特殊的二值性訊號量,用於實現對共享資源的獨佔式處理。
任意時刻互斥鎖的狀態只有兩種,開鎖或閉鎖。當有任務持有時,互斥鎖處於閉鎖狀態,這個任務獲得該互斥鎖的所有權。當該任務釋放它時,該互斥鎖被開鎖,任務失去該互斥鎖的所有權。當一個任務持有互斥鎖時,其他任務將不能再對該互斥鎖進行開鎖或持有。
多工環境下往往存在多個任務競爭同一共享資源的應用場景,互斥鎖可被用於對共
享資源的保護從而實現獨佔式訪問。另外,互斥鎖可以解決訊號量存在的優先順序翻轉
問題。
Huawei LiteOS提供的互斥鎖具有如下特點:
- 通過優先順序繼承演算法,解決優先順序翻轉問題。
1.2 運作機制
1.2.1 互斥鎖運作原理
多工環境下會存在多個任務訪問同一公共資源的場景,而有些公共資源是非共享的,需要任務進行獨佔式處理。互斥鎖怎樣來避免這種衝突呢?
用互斥鎖處理非共享資源的同步訪問時,如果有任務訪問該資源,則互斥鎖為加鎖狀態。此時其他任務如果想訪問這個公共資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其他任務才能重新訪問該公共資源,此時互斥鎖再次上鎖,如此確保同一時刻只有一個任務正在訪問這個公共資源,保證了公共資源操作的完整性。
1.3 開發指導
1.3.1 使用場景
互斥鎖可以提供任務之間的互斥機制,用來防止兩個任務在同一時刻訪問相同的共享資源
1.3.2 功能
Huawei LiteOS 系統中的互斥鎖模組為使用者提供下面幾種功能。
功能分類 | 介面名 | 描述 |
---|---|---|
互斥鎖的建立和刪除 | LOS_MuxCreate | 建立互斥鎖 |
== | LOS_MuxDelete | 刪除指定的互斥鎖 |
互斥鎖的申請和釋放 | LOS_MuxPend | 申請指定的互斥鎖 |
== | LOS_MuxPost | 釋放指定的互斥鎖 |
1.3.3 開發流程
互斥鎖典型場景的開發流程:
- 建立互斥鎖LOS_MuxCreate。
- 申請互斥鎖LOS_MuxPend。
申請模式有三種:無阻塞模式、永久阻塞模式、定時阻塞模式。
- 無阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有任務持有,或者持有該互斥鎖的任務和申請該互斥鎖的任務為同一個任務,則申請成功
- 永久阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則,該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,直到有其他任務釋放該互斥鎖,阻塞任務才會重新得以執行
- 定時阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,指定時間超時前有其他任務釋放該互斥鎖,或者使用者指定時間超時後,阻塞任務才會重新得以執行
- 釋放互斥鎖LOS_MuxPost。
- 如果有任務阻塞於指定互斥鎖,則喚醒最早被阻塞的任務,該任務進入就緒態,並進行任務排程;
- 如果沒有任務阻塞於指定互斥鎖,則互斥鎖釋放成功。
- 刪除互斥鎖LOS_MuxDelete。
1.3.4 互斥鎖錯誤碼
對互斥鎖存在失敗的可能性操作,包括互斥鎖建立,互斥鎖刪除,互斥鎖申請,互斥鎖釋放
序號 | 定義 | 實際數值 | 描述 | 參考解決方案 |
---|---|---|---|---|
1 | LOS_ERRNO_MUX_NO_MEMORY | 0x02001d00 | 記憶體請求失敗 | 減少互斥鎖限制數量的上限 |
2 | LOS_ERRNO_MUX_INVALID | 0x02001d01 | 互斥鎖不可用 | 傳入有效的互斥鎖的ID |
3 | LOS_ERRNO_MUX_PTR_NULL | 0x02001d02 | 入參為空 | 確保入參可用 |
4 | LOS_ERRNO_MUX_ALL_BUSY | 0x02001d03 | 沒有互斥鎖可用 | 增加互斥鎖限制數量的上限 |
5 | LOS_ERRNO_MUX_UNAVAILABLE | 0x02001d04 | 鎖失敗,因為鎖被其他執行緒使用 | 等待其他執行緒解鎖或者設定等待時間 |
6 | LOS_ERRNO_MUX_PEND_INTERR | 0x02001d05 | 在中斷中使用互斥鎖 | 在中斷中禁止呼叫此介面 |
7 | LOS_ERRNO_MUX_PEND_IN_LOCK | 0x02001d06 | 任務排程沒有使能,執行緒等待另一個執行緒釋放鎖 | 設定PEND為非阻塞模式或者使能任務排程 |
8 | LOS_ERRNO_MUX_TIMEOUT | 0x02001d07 | 互斥鎖PEND超時 | 增加等待時間或者設定一直等待模式 |
9 | LOS_ERRNO_MUX_OVERFLOW | 0x02001d08 | 暫未使用,待擴充套件 | 無 |
10 | LOS_ERRNO_MUX_PENDED | 0x02001d09 | 刪除正在使用的鎖 | 等待解鎖再刪除鎖 |
11 | LOS_ERRNO_MUX_GET_COUNT_ERR | 0x02001d0a | 暫未使用,待擴充套件 | 無 |
12 | LOS_ERRNO_MUX_REG_ERROR | 0x02001d0b | 暫未使用,待擴充套件 | 無 |
錯誤碼定義:錯誤碼是一個32位的儲存單元, 31~24位表示錯誤等級, 23~16位表示錯誤碼標誌, 15~8位代表錯誤碼所屬模組, 7~0位表示錯誤碼序號,如下
#define LOS_ERRNO_OS_ERROR(MID, ERRNO) \
(LOS_ERRTYPE_ERROR | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
LOS_ERRTYPE_ERROR: Define critical OS errors
LOS_ERRNO_OS_ID: OS error code flag
LOS_MOD_MUX: Mutex module ID
MID: OS_MOUDLE_ID
ERRNO: error ID number
例如:
LOS_ERRNO_MUX_TIMEOUT LOS_ERRNO_OS_ERROR(LOS_MOD_MUX, 0x07)
1.3.5 平臺差異性
1.4 注意事項
- 兩個任務不能對同一把互斥鎖加鎖。如果某任務對已被持有的互斥鎖加鎖,則該任務會被掛起,直到持有該鎖的任務對互斥鎖解鎖,才能執行對這把互斥鎖的加鎖操作。
- 互斥鎖不能在中斷服務程式中使用。
- Huawei LiteOS作為實時作業系統需要保證任務排程的實時性,儘量避免任務的長時間阻塞,因此在獲得互斥鎖之後,應該儘快釋放互斥鎖。
- 持有互斥鎖的過程中,不得再呼叫LOS_TaskPriSet等介面更改持有互斥鎖任務的優先順序。
1.5 程式設計例項
1.5.1 例項描述
本例項實現如下流程。
- 任務Example_TaskEntry建立一個互斥鎖,鎖任務排程,建立兩個任務Example_MutexTask1、 Example_MutexTask2,Example_MutexTask2優先順序高於Example_MutexTask1,解鎖任務排程。
- Example_MutexTask2被排程,永久申請互斥鎖,然後任務休眠100Tick,Example_MutexTask2掛起, Example_MutexTask1被喚醒。
- Example_MutexTask1申請互斥鎖,等待時間為10Tick,因互斥鎖仍被Example_MutexTask2持有, Example_MutexTask1掛起, 10Tick後未拿到互斥鎖,Example_MutexTask1被喚醒,試圖以永久等待申請互斥鎖, Example_MutexTask1掛起。
- 100Tick後Example_MutexTask2喚醒, 釋放互斥鎖後, Example_MutexTask1被排程執行,最後釋放互斥鎖。
- Example_MutexTask1執行完, 300Tick後任務Example_TaskEntry被排程執行,刪除互斥鎖
1.5.2 程式設計示例
前提條件:
- 在los_config.h中,將OS_INCLUDE_MUX配置項開啟。
- 配好OS_MUX_MAX_SUPPORT_NUM最大的互斥鎖個數
程式碼實現如下:
#include "los_mux.h
#include "los_task.h"
/*互斥鎖控制程式碼ID*/
MUX_HANDLE_T g_Testmux01
/*任務PID*/
UINT32 g_TestTaskID01;
UINT32 g_TestTaskID02;
VOID Example_MutexTask1()
{
UINT32 uwRet;
printf("task1 try to get mutex, wait 10 Tick.\n");
/*申請互斥鎖*/
uwRet=LOS_MuxPend(g_Testmux01, 10);
if(uwRet == LOS_OK)
{
printf("task1 get mutex g_Testmux01.\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
return;
}
else if(uwRet == LOS_ERRNO_MUX_TIMEOUT )
{
printf("task1 timeout and try to get mutex, wait forever.\n");
/*申請互斥鎖*/
uwRet = LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
if(uwRet == LOS_OK)
{
printf("task1 wait forever,get mutex g_Testmux01.\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
return;
}
}
return;
}
VOID Example_MutexTask2()
{
UINT32 uwRet;
printf("task2 try to get mutex, wait forever.\n");
/*申請互斥鎖*/
uwRet=LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
printf("task2 get mutex g_Testmux01 and suspend 100 Tick.\n");
/*任務休眠100 Tick*/
LOS_TaskDelay(100);
printf("task2 resumed and post the g_Testmux01\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
return;
}
UINT32 Example_TaskEntry()
{
UINT32 uwRet;
TSK_INIT_PARAM_S stTask1;
TSK_INIT_PARAM_S stTask2;
/*建立互斥鎖*/
LOS_MuxCreate(&g_Testmux01);
/*鎖任務排程*/
LOS_TaskLock();
/*建立任務1*/
memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
stTask1.pcName = "MutexTsk1";
stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
stTask1.usTaskPrio = 5;
uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
if(uwRet != LOS_OK)
{
printf("task1 create failed .\n");
return LOS_NOK;
}
/*建立任務2*/
memset(&stTask2, 0, sizeof(TSK_INIT_PARAM_S));
stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
stTask2.pcName = "MutexTsk2";
stTask2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
stTask2.usTaskPrio = 4;
uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
if(uwRet != LOS_OK)
{
printf("task2 create failed .\n");
return LOS_NOK;
}
/*解鎖任務排程*/
LOS_TaskUnlock();
/*任務休眠300 Tick*/
LOS_TaskDelay(300);
/*刪除互斥鎖*/
LOS_MuxDelete(g_Testmux01);
/*刪除任務1*/
uwRet = LOS_TaskDelete(g_TestTaskID01);
if(uwRet != LOS_OK)
{
printf("task1 delete failed .\n");
return LOS_NOK;
}
/*刪除任務2*/
uwRet = LOS_TaskDelete(g_TestTaskID02);
if(uwRet != LOS_OK)
{
printf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
1.5.3 結果驗證
編譯執行得到的結果為:
task2 try to get mutex, wait forever.
task2 get mutex g_Testmux01 and suspend 100 ticks.
task1 try to get mutex, wait 10 ticks.
task1 timeout and try to get mutex, wait forever.
task2 resumed and post the g_Testmux01
task1 wait forever,get mutex g_Testmux01