信號量(Semaphore)是一種實現任務間通訊的機制,實現任務之間同步或臨界資源的互斥訪問。經常使用於協助一組相互競爭的任務來訪問臨界資源。編程
在多任務系統中,各任務之間須要同步或互斥實現臨界資源的保護,信號量功能能夠爲用戶提供這方面的支持。安全
一般一個信號量的計數值用於對應有效的資源數,表示剩下的可被佔用的互斥資源數。其值的含義分兩種狀況:post
以同步爲目的的信號量和以互斥爲目的的信號量在使用有以下不一樣:測試
/** * @ingroup los_sem * Semaphore control structure. */ typedef struct { UINT8 usSemStat; /**是否使用標誌位*/ UINT16 uwSemCount; /**信號量索引號*/ UINT32 usSemID; /**信號量計數*/ LOS_DL_LIST stSemList; /**掛接阻塞於該信號量的任務*/ }SEM_CB_S;
信號量初始化,爲配置的N個信號量申請內存(N值能夠由用戶自行配置,受內存限制,詳見第十章 配置參考),並把全部的信號量初始化成未使用,並加入到未使用鏈表中供系統使用。指針
信號量建立,從未使用的信號量鏈表中獲取一個信號量資源,並設定初值。code
信號量申請,若其計數器值大於0,則直接減1返回成功。不然任務阻塞,等待其它任務釋放該信號量,等待的超時時間可設定。當任務被一個信號量阻塞時,將該任務掛到信號量等待任務隊列的隊尾。索引
信號量釋放,若沒有任務等待該信號量,則直接將計數器加1返回。不然喚醒該信號量等待任務隊列上的第一個任務。接口
信號量刪除,將正在使用的信號量置爲未使用信號量,並掛回到未使用鏈表。隊列
信號量容許多個任務在同一時刻訪問同一資源,但會限制同一時刻訪問此資源的最大任務數目。訪問同一資源的任務數達到該資源的最大數量時,會阻塞其餘試圖獲取該資源的任務,直到有任務釋放該信號量。內存
信號量是一種很是靈活的同步方式,能夠運用在多種場合中,實現鎖、同步、資源計數等功能,也能方便的用於任務與任務,中斷與任務的同步中。
Huawei LiteOS 系統中的信號量模塊爲用戶提供下面幾種功能。
功能分類 | 接口名 | 描述 |
---|---|---|
信號量的建立和刪除 | LOS_SemCreate | 建立信號量 |
- | LOS_SemDelete | 刪除指定的信號量 |
信號量的申請和釋放 | LOS_SemPend | 申請指定的信號量 |
- | LOS_SemPost | 釋放指定的信號量 |
信號量的開發典型流程:
對可能致使信號量操做失敗的狀況,包括建立信號量、申請信號量、釋放信號量、刪除信號量等,均須要返回對應的錯誤碼,以便快速定位錯誤緣由。
序 號 | 定義 | 實際數值 | 描述 | 參考解決方案 |
---|---|---|---|---|
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))
本實例實現以下功能;
前提條件:
代碼實現以下
#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; }
編譯運行獲得的結果爲:
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