liteos信號量(八)

1. 概述

1.1 基本概念

信號量(Semaphore)是一種實現任務間通訊的機制,實現任務之間同步或臨界資源的互斥訪問。經常使用於協助一組相互競爭的任務來訪問臨界資源。編程

在多任務系統中,各任務之間須要同步或互斥實現臨界資源的保護,信號量功能能夠爲用戶提供這方面的支持。安全

一般一個信號量的計數值用於對應有效的資源數,表示剩下的可被佔用的互斥資源數。其值的含義分兩種狀況:post

  • 0,表示沒有積累下來的Post操做,且有可能有在此信號量上阻塞的任務。
  • 正值,表示有一個或多個Post下來的釋放操做。

以同步爲目的的信號量和以互斥爲目的的信號量在使用有以下不一樣:測試

  • 用做互斥時,信號量建立後記數是滿的,在須要使用臨界資源時,先取信號量,使其變空,這樣其餘任務須要使用臨界資源時就會由於沒法取到信號量而阻塞,從而保證了臨界資源的安全。
  • 用做同步時,信號量在建立後被置爲空,任務1取信號量而阻塞,任務2在某種條件發生後,釋放信號量,因而任務1得以進入READY或RUNNING態,從而達到了兩個任務間的同步。

1.2 運做機制

1.2.1 信號量控制塊

/**
* @ingroup los_sem
* Semaphore control structure.
*/
typedef struct
{
    UINT8 usSemStat; /**是否使用標誌位*/
    UINT16 uwSemCount; /**信號量索引號*/
    UINT32 usSemID; /**信號量計數*/
    LOS_DL_LIST stSemList; /**掛接阻塞於該信號量的任務*/
}SEM_CB_S;

1.2.2 信號量運做原理

信號量初始化,爲配置的N個信號量申請內存(N值能夠由用戶自行配置,受內存限制,詳見第十章 配置參考),並把全部的信號量初始化成未使用,並加入到未使用鏈表中供系統使用。指針

信號量建立,從未使用的信號量鏈表中獲取一個信號量資源,並設定初值。code

信號量申請,若其計數器值大於0,則直接減1返回成功。不然任務阻塞,等待其它任務釋放該信號量,等待的超時時間可設定。當任務被一個信號量阻塞時,將該任務掛到信號量等待任務隊列的隊尾。索引

信號量釋放,若沒有任務等待該信號量,則直接將計數器加1返回。不然喚醒該信號量等待任務隊列上的第一個任務。接口

信號量刪除,將正在使用的信號量置爲未使用信號量,並掛回到未使用鏈表。隊列

信號量容許多個任務在同一時刻訪問同一資源,但會限制同一時刻訪問此資源的最大任務數目。訪問同一資源的任務數達到該資源的最大數量時,會阻塞其餘試圖獲取該資源的任務,直到有任務釋放該信號量。內存

2. 開發指導

2.1 使用場景

信號量是一種很是靈活的同步方式,能夠運用在多種場合中,實現鎖、同步、資源計數等功能,也能方便的用於任務與任務,中斷與任務的同步中。

2.2 功能

Huawei LiteOS 系統中的信號量模塊爲用戶提供下面幾種功能。

功能分類 接口名 描述
信號量的建立和刪除 LOS_SemCreate 建立信號量
- LOS_SemDelete 刪除指定的信號量
信號量的申請和釋放 LOS_SemPend 申請指定的信號量
- LOS_SemPost 釋放指定的信號量

2.3 開發流程

信號量的開發典型流程:

  1. 建立信號量LOS_SemCreate。
  2. 申請信號量LOS_SemPend。
    信號量有三種申請模式:無阻塞模式、永久阻塞模式、定時阻塞模式
  • 無阻塞模式:任務須要申請信號量,若當前信號量的任務數沒有到信號量設定的上限,則申請成功。不然,當即返回申請失敗
  • 永久阻塞模式:任務須要申請信號量,若當前信號量的任務數沒有到信號量設定的上限,則申請成功。不然,該任務進入阻塞態,系統切換到就緒任務中優先級最高者繼續執行。任務進入阻塞態後,直到有其餘任務釋放該信號量,阻塞任務纔會從新得以執行
  • 定時阻塞模式:任務須要申請信號量,若當前信號量的任務數沒有到信號量設定的上限,則申請成功。不然,該任務進入阻塞態,系統切換到就緒任務中優先級最高者繼續執行。任務進入阻塞態後,指定時間超時前有其餘任務釋放該信號量,或者用戶指定時間超時後,阻塞任務纔會從新得以執行
  1. 釋放信號量LOS_SemPost。
  • 若是有任務阻塞於指定信號量,則喚醒該信號量阻塞隊列上的第一個任務。該任務進入就緒態,並進行調度
  • 若是沒有任務阻塞於指定信號量,釋放信號量成功
  1. 刪除信號量LOS_SemDelet

2.4 信號量錯誤碼

對可能致使信號量操做失敗的狀況,包括建立信號量、申請信號量、釋放信號量、刪除信號量等,均須要返回對應的錯誤碼,以便快速定位錯誤緣由。

序 號 定義 實際數值 描述 參考解決方案
1 LOS_ERRNO_SEM_NO_MEMORY 0x02000700 內存空間不足 分配更大的內存分區
2 LOS_ERRNO_SEM_INVALID 0x02000701 非法傳參 改變傳數爲合法值
3 LOS_ERRNO_SEM_PTR_NULL 0x02000702 傳入空指針 傳入合法指針
4 LOS_ERRNO_SEM_ALL_BUSY 0x02000703 信號量控制塊不可用 釋放資源信號量資源
5 LOS_ERRNO_SEM_UNAVAILABLE 0x02000704 定時時間非法 傳入正確的定時時間
6 LOS_ERRNO_SEM_PEND_INTERR 0x02000705 中斷期間非法調用LOS_SemPend 中斷期間禁止調用LOS_SemPend
7 LOS_ERRNO_SEM_PEND_IN_LOCK 0x02000706 任務被鎖,沒法得到信號量 在任務被鎖時,不能調用LOS_SemPend
8 LOS_ERRNO_SEM_TIMEOUT 0x02000707 獲取信號量時間超時 將時間設置在合理範圍內

錯誤碼定義:錯誤碼是一個32位的存儲單元, 31~24位表示錯誤等級, 23~16位表示錯誤碼標誌, 15~8位表明錯誤碼所屬模塊, 7~0位表示錯誤碼序號,以下

#define LOS_ERRNO_OS_NORMAL(MID,ERRNO) \
(LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
LOS_ERRTYPE_NORMAL :Define the error level as critical
LOS_ERRNO_OS_ID :OS error code flag.
MID:OS_MOUDLE_ID
ERRNO:error ID number

例如:

LOS_ERRNO_SEM_NO_MEMORY LOS_ERRNO_OS_ERROR(LOS_MOD_SEM, 0x00))

2.5 平臺差別性

2.6 注意事項

  • 因爲中斷不能被阻塞,所以在申請信號量時,阻塞模式不能在中斷中使用。

3. 編程實例

3.1 實例描述

本實例實現以下功能;

  1. 測試任務Example_TaskEntry建立一個信號量,鎖任務調度,建立兩個任務Example_SemTask一、 Example_SemTask2,Example_SemTask2優先級高於Example_SemTask1,兩個任務中申請同一信號量,解鎖任務調度後兩任務阻塞,測試任務Example_TaskEntry釋放信號量。
  2. Example_SemTask2獲得信號量,被調度,而後任務休眠20Tick,Example_SemTask2延遲, Example_SemTask1被喚醒。
  3. Example_SemTask1定時阻塞模式申請信號量,等待時間爲10Tick,因信號量仍被Example_SemTask2持有, Example_SemTask1掛起, 10Tick後仍未獲得信號量,Example_SemTask1被喚醒,試圖以永久阻塞模式申請信號量, Example_SemTask1掛起。
  4. 20Tick後Example_SemTask2喚醒, 釋放信號量後, Example_SemTask1獲得信號量被調度運行,最後釋放信號量。
  5. Example_SemTask1執行完, 40Tick後任務Example_TaskEntry被喚醒,執行刪除信號量,刪除兩個任務。

3.2 編程示例

前提條件:

  • 在los_config.h中,將LOSCFG_BASE_IPC_SEM配置爲YES。
  • 配置用戶定義的LOSCFG_BASE_IPC_SEM_LIMIT最大的信號量數,如1024。

代碼實現以下

#include "los_sem.h"
/*任務PID*/
static UINT32 g_TestTaskID01,g_TestTaskID02;
/*測試任務優先級*/
#define TASK_PRIO_TEST 5
/*信號量結構體ID*/
static SEM_HANDLE_T g_usSemID;
VOID Example_SemTask1(void)
{
    UINT32 uwRet;
    printf("Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.\n");
    /*定時阻塞模式申請信號量,定時時間爲10Tick*/
    uwRet = LOS_SemPend(g_usSemID, 10);
    /*申請到信號量*/
    if(LOS_OK == uwRet)
    {
        LOS_SemPost(g_usSemID);
        return;
    }
    /*定時時間到,未申請到信號量*/
    if(LOS_ERRNO_SEM_TIMEOUT == uwRet)
    {
        printf("Example_SemTask1 timeout and try get sem g_usSemID wait forever.\n");
        /*永久阻塞模式申請信號量*/
        uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
        printf("Example_SemTask1 wait_forever and get sem g_usSemID .\n");
        if(LOS_OK == uwRet)
        {
            LOS_SemPost(g_usSemID);
            return;
        }
    }
    return;
}
VOID Example_SemTask2(void)
{
    UINT32 uwRet;
    printf("Example_SemTask2 try get sem g_usSemID wait forever.\n");
    /*永久阻塞模式申請信號量*/
    uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
    if(LOS_OK == uwRet)
        printf("Example_SemTask2 get sem g_usSemID and then delay 20ticks .\n");
        /*任務休眠20 Tick*/
        LOS_TaskDelay(20);
        printf("Example_SemTask2 post sem g_usSemID .\n");
        /*釋放信號量*/
        LOS_SemPost(g_usSemID);
        return;
}
UINT32 Example_TaskEntry()
{
    UINT32 uwRet;
    TSK_INIT_PARAM_S stTask1;
    TSK_INIT_PARAM_S stTask2;
    /*建立信號量*/
    LOS_SemCreate(0,&g_usSemID);
    /*鎖任務調度*/
    LOS_TaskLock();
    /*建立任務1*/
    memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
    stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask1;
    stTask1.pcName = "MutexTsk1";
    stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
    stTask1.usTaskPrio = TASK_PRIO_TEST;
    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_SemTask2;
    stTask2.pcName = "MutexTsk2";
    stTask2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
    stTask2.usTaskPrio = (TASK_PRIO_TEST - 1);
    uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
    if(uwRet != LOS_OK)
    {
        printf("task2 create failed .\n");
        return LOS_NOK;
    }
    /*解鎖任務調度*/
    LOS_TaskUnlock();
    uwRet = LOS_SemPost(g_usSemID);
    /*任務休眠40 Tick*/
    LOS_TaskDelay(40);
    /*刪除信號量*/
    LOS_SemDelete(g_usSemID);
    /*刪除任務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;
}

3.3 結果驗證

編譯運行獲得的結果爲:

Example_SemTask2 try get sem g_usSemID wait forever.
Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.
Example_SemTask2 get sem g_usSemID and then delay 20ticks .
Example_SemTask1 timeout and try get sem g_usSemID wait forever.
Example_SemTask2 post sem g_usSemID .
Example_SemTask1 wait_forever and get sem g_usSemID
相關文章
相關標籤/搜索