1. 程式人生 > >liteos實時作業系統---互斥鎖

liteos實時作業系統---互斥鎖

概述

基本概念

互斥鎖又稱互斥型訊號量,是一種特殊的二值性訊號量,用於實現對共享資源的獨佔式處理。

任意時刻互斥鎖的狀態只有兩種,開鎖或閉鎖。當有任務持有時,互斥鎖處於閉鎖狀態,這個任務獲得該互斥鎖的所有權。當該任務釋放它時,該互斥鎖被開鎖,任務失去該互斥鎖的所有權。當一個任務持有互斥鎖時,其他任務將不能再對該互斥鎖進行開鎖或持有。

多工環境下往往存在多個任務競爭同一共享資源的應用場景,互斥鎖可被用於對共享資源的保護從而實現獨佔式訪問。另外,互斥鎖可以解決訊號量存在的優先順序翻轉問題。

Huawei LiteOS提供的互斥鎖具有如下特點:

  • 通過優先順序繼承演算法,解決優先順序翻轉問題。

互斥鎖運作原理

多工環境下會存在多個任務訪問同一公共資源的場景,而有些公共資源是非共享的,需要任務進行獨佔式處理。互斥鎖怎樣來避免這種衝突呢?

用互斥鎖處理非共享資源的同步訪問時,如果有任務訪問該資源,則互斥鎖為加鎖狀態。此時其他任務如果想訪問這個公共資源則會被阻塞,直到互斥鎖被持有該鎖的任務釋放後,其他任務才能重新訪問該公共資源,此時互斥鎖再次上鎖,如此確保同一時刻只有一個任務正在訪問這個公共資源,保證了公共資源操作的完整性。

使用場景

互斥鎖可以提供任務之間的互斥機制,用來防止兩個任務在同一時刻訪問相同的共享資源。

Huawei LiteOS 系統中的互斥鎖模組為使用者提供下面幾種功能。

功能分類

介面名

描述

互斥鎖的建立和刪除

LOS_MuxCreate

建立互斥鎖

LOS_MuxDelete

刪除指定的互斥鎖

互斥鎖的申請和釋放

LOS_MuxPend

申請指定的互斥鎖

LOS_MuxPost

釋放指定的互斥鎖

互斥鎖典型場景的開發流程:

  1. 建立互斥鎖LOS_MuxCreate。
  2. 申請互斥鎖LOS_MuxPend。

    申請模式有三種:無阻塞模式、永久阻塞模式、定時阻塞模式。

    • 無阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有任務持有,或者持有該互斥鎖的任務和申請該互斥鎖的任務為同一個任務,則申請成功
    • 永久阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則,該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,直到有其他任務釋放該互斥鎖,阻塞任務才會重新得以執行
    • 定時阻塞模式:任務需要申請互斥鎖,若該互斥鎖當前沒有被佔用,則申請成功。否則該任務進入阻塞態,系統切換到就緒任務中優先順序最高者繼續執行。任務進入阻塞態後,指定時間超時前有其他任務釋放該互斥鎖,或者使用者指定時間超時後,阻塞任務才會重新得以執行
  3. 釋放互斥鎖LOS_MuxPost。
    • 如果有任務阻塞於指定互斥鎖,則喚醒最早被阻塞的任務,該任務進入就緒態,並進行任務排程;
    • 如果沒有任務阻塞於指定互斥鎖,則互斥鎖釋放成功。
  4. 刪除互斥鎖LOS_MuxDelete。

程式設計例項

本例項實現如下流程。

  1. 任務Example_TaskEntry建立一個互斥鎖,鎖任務排程,建立兩個任務Example_MutexTask1、Example_MutexTask2,Example_MutexTask2優先順序高於Example_MutexTask1,解鎖任務排程。
  2. Example_MutexTask2被排程,永久申請互斥鎖,然後任務休眠100Tick,Example_MutexTask2掛起,Example_MutexTask1被喚醒。
  3. Example_MutexTask1申請互斥鎖,等待時間為10Tick,因互斥鎖仍被Example_MutexTask2持有,Example_MutexTask1掛起,10Tick後未拿到互斥鎖,Example_MutexTask1被喚醒,試圖以永久等待申請互斥鎖,Example_MutexTask1掛起。
  4. 100Tick後Example_MutexTask2喚醒, 釋放互斥鎖後,Example_MutexTask1被排程執行,最後釋放互斥鎖。
  5. 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等介面更改持有互斥鎖任務的優先順序。