信号量(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