liteos互斥鎖(七)

yooooooo發表於2019-05-26

1. 概述

1.1 基本概念

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

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

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

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

1.2 運作機制

1.2.1 互斥鎖運作原理

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

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

liteos互斥鎖(七)

1.3 開發指導

1.3.1 使用場景

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

1.3.2 功能

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

功能分類 介面名 描述
互斥鎖的建立和刪除 LOS_MuxCreate 建立互斥鎖
== LOS_MuxDelete 刪除指定的互斥鎖
互斥鎖的申請和釋放 LOS_MuxPend 申請指定的互斥鎖
== LOS_MuxPost 釋放指定的互斥鎖

1.3.3 開發流程

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

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

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

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

本例項實現如下流程。

  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被排程執行,刪除互斥鎖

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

相關文章