liteos實時作業系統---互斥鎖
阿新 • • 發佈:2018-12-10
概述
基本概念
互斥鎖又稱互斥型訊號量,是一種特殊的二值性訊號量,用於實現對共享資源的獨佔式處理。
任意時刻互斥鎖的狀態只有兩種,開鎖或閉鎖。當有任務持有時,互斥鎖處於閉鎖狀態,這個任務獲得該互斥鎖的所有權。當該任務釋放它時,該互斥鎖被開鎖,任務失去該互斥鎖的所有權。當一個任務持有互斥鎖時,其他任務將不能再對該互斥鎖進行開鎖或持有。
多工環境下往往存在多個任務競爭同一共享資源的應用場景,互斥鎖可被用於對共享資源的保護從而實現獨佔式訪問。另外,互斥鎖可以解決訊號量存在的優先順序翻轉問題。
Huawei LiteOS提供的互斥鎖具有如下特點:
- 通過優先順序繼承演算法,解決優先順序翻轉問題。
互斥鎖運作原理
多工環境下會存在多個任務訪問同一公共資源的場景,而有些公共資源是非共享的,需要任務進行獨佔式處理。互斥鎖怎樣來避免這種衝突呢?
用互斥鎖處理非共享資源的同步訪問時,如果有任務訪問該資源,則互斥鎖為加鎖狀態。此時其他任務如果想訪問這個公共資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其他任務才能重新訪問該公共資源,此時互斥鎖再次上鎖,如此確保同一時刻只有一個任務正在訪問這個公共資源,保證了公共資源操作的完整性。
使用場景
互斥鎖可以提供任務之間的互斥機制,用來防止兩個任務在同一時刻訪問相同的共享資源。
Huawei LiteOS 系統中的互斥鎖模組為使用者提供下面幾種功能。
功能分類 |
介面名 |
描述 |
---|---|---|
互斥鎖的建立和刪除 |
LOS_MuxCreate |
建立互斥鎖 |
LOS_MuxDelete |
刪除指定的互斥鎖 |
|
互斥鎖的申請和釋放 |
LOS_MuxPend |
申請指定的互斥鎖 |
LOS_MuxPost |
釋放指定的互斥鎖 |
互斥鎖典型場景的開發流程:
- 建立互斥鎖LOS_MuxCreate。
- 申請互斥鎖LOS_MuxPend。
申請模式有三種:無阻塞模式、永久阻塞模式、定時阻塞模式。
- 無阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有任務持有,或者持有該互斥鎖的任務和申請該互斥鎖的任務為同一個任務,則申請成功
- 永久阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則,該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,直到有其他任務釋放該互斥鎖,阻塞任務才會重新得以執行
- 定時阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,指定時間超時前有其他任務釋放該互斥鎖,或者使用者指定時間超時後,阻塞任務才會重新得以執行
- 釋放互斥鎖LOS_MuxPost。
- 如果有任務阻塞於指定互斥鎖,則喚醒最早被阻塞的任務,該任務進入就緒態,並進行任務排程;
- 如果沒有任務阻塞於指定互斥鎖,則互斥鎖釋放成功。
- 刪除互斥鎖LOS_MuxDelete。
程式設計例項
本例項實現如下流程。
- 任務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被排程執行,刪除互斥鎖。
前提條件:
- 在los_config.h中,將LOSCFG_BASE_IPC_MUX配置項開啟。
- 配好LOSCFG_BASE_IPC_MUX_LIMIT最大的互斥鎖個數。
/*互斥鎖控制代碼ID*/
static UINT32 g_Testmux01;
/*任務PID*/
UINT32 g_TestTaskID01;
UINT32 g_TestTaskID02;
static VOID Example_MutexTask1()
{
UINT32 uwRet;
dprintf("task1 try to get mutex, wait 10 Tick.\n");
/*申請互斥鎖*/
uwRet=LOS_MuxPend(g_Testmux01, 10);
if(uwRet == LOS_OK)
{
dprintf("task1 get mutex g_Testmux01.\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
return;
}
else if(uwRet == LOS_ERRNO_MUX_TIMEOUT )
{
dprintf("task1 timeout and try to get mutex, wait forever.\n");
/*LOS_WAIT_FOREVER方式申請互斥鎖,獲取不到時程式阻塞,不會返回*/
uwRet = LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
if(uwRet == LOS_OK)
{
dprintf("task1 wait forever,got mutex g_Testmux01 success.\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
uwRet = LOS_InspectStatusSetByID(LOS_INSPECT_MUTEX,LOS_INSPECT_STU_SUCCESS);
if (LOS_OK != uwRet)
{
dprintf("Set Inspect Status Err\n");
}
return;
}
}
return;
}
static VOID Example_MutexTask2()
{
UINT32 uwRet;
dprintf("task2 try to get mutex, wait forever.\n");
/*申請互斥鎖*/
uwRet=LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
if(uwRet != LOS_OK)
{
dprintf("task2 LOS_MuxPend failed .\n");
return;
}
dprintf("task2 get mutex g_Testmux01 and suspend 100 Tick.\n");
/*任務休眠100 Tick*/
LOS_TaskDelay(100);
dprintf("task2 resumed and post the g_Testmux01\n");
/*釋放互斥鎖*/
LOS_MuxPost(g_Testmux01);
return;
}
UINT32 Example_MutexLock(VOID)
{
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 = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
stTask1.usTaskPrio = 5;
uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
if(uwRet != LOS_OK)
{
dprintf("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 = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
stTask2.usTaskPrio = 4;
uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
if(uwRet != LOS_OK)
{
dprintf("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)
{
dprintf("task1 delete failed .\n");
return LOS_NOK;
}
/*刪除任務2*/
uwRet = LOS_TaskDelete(g_TestTaskID02);
if(uwRet != LOS_OK)
{
dprintf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
注意事項
- 兩個任務不能對同一把互斥鎖加鎖。如果某任務對已被持有的互斥鎖加鎖,則該任務會被掛起,直到持有該鎖的任務對互斥鎖解鎖,才能執行對這把互斥鎖的加鎖操作。
- 互斥鎖不能在中斷服務程式中使用。
- Huawei LiteOS作為實時作業系統需要保證任務排程的實時性,儘量避免任務的長時間阻塞,因此在獲得互斥鎖之後,應該儘快釋放互斥鎖。
- 持有互斥鎖的過程中,不得再呼叫LOS_TaskPriSet等介面更改持有互斥鎖任務的優先順序。