1# 信号量<a name="ZH-CN_TOPIC_0000001078912740"></a> 2 3- [基本概念](#section1577111168131) 4- [运行机制](#section118423019134) 5- [开发指导](#section01419503131) 6 - [接口说明](#section1232345431312) 7 - [开发流程](#section154261711141419) 8 - [编程实例](#section658135571417) 9 - [实例描述](#section125244411653) 10 - [编程示例](#section1742105514512) 11 - [结果验证](#section11297301617) 12 13 14## 基本概念<a name="section1577111168131"></a> 15 16信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。 17 18一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况: 19 20- 0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。 21- 正值,表示该信号量当前可被获取。 22 23以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同: 24 25- 用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量,一种类似于互斥锁的机制。 26- 用作同步时,初始信号量计数值为0。任务1获取信号量而阻塞,直到任务2或者某中断释放信号量,任务1才得以进入Ready或Running态,从而达到了任务间的同步。 27 28## 运行机制<a name="section118423019134"></a> 29 30**信号量控制块** 31 32``` 33/** 34 * 信号量控制块数据结构 35 */ 36typedef struct { 37 UINT16 semStat; /* 信号量状态 */ 38 UINT16 semType; /* 信号量类型 */ 39 UINT16 semCount; /* 信号量计数 */ 40 UINT16 semId; /* 信号量索引号 */ 41 LOS_DL_LIST semList; /* 挂接阻塞于该信号量的任务 */ 42} LosSemCB; 43``` 44 45**信号量运作原理** 46 47信号量允许多个任务在同一时刻访问共享资源,但会限制同一时刻访问此资源的最大任务数目。当访问资源的任务数达到该资源允许的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。 48 49- 信号量初始化 50 51 初始化时为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG\_BASE\_IPC\_SEM\_LIMIT宏实现),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用 52 53- 信号量创建 54 55 从未使用的信号量链表中获取一个信号量,并设定初值。 56 57- 信号量申请 58 59 若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。 60 61- 信号量释放 62 63 若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。 64 65- 信号量删除 66 67 将正在使用的信号量置为未使用信号量,并挂回到未使用链表。 68 69 70运行示意图如下图所示: 71 72**图 1** 信号量运作示意图<a name="fig467314634214"></a> 73 74 75## 开发指导<a name="section01419503131"></a> 76 77### 接口说明<a name="section1232345431312"></a> 78 79**表 1** 信号量模块接口 80 81<a name="table1415203765610"></a> 82<table><thead align="left"><tr id="row134151837125611"><th class="cellrowborder" valign="top" width="12.85128512851285%" id="mcps1.2.4.1.1"><p id="p16415637105612"><a name="p16415637105612"></a><a name="p16415637105612"></a>功能分类</p> 83</th> 84<th class="cellrowborder" valign="top" width="29.8029802980298%" id="mcps1.2.4.1.2"><p id="p11415163718562"><a name="p11415163718562"></a><a name="p11415163718562"></a>接口<strong id="b197068338312"><a name="b197068338312"></a><a name="b197068338312"></a>名称</strong></p> 85</th> 86<th class="cellrowborder" valign="top" width="57.34573457345735%" id="mcps1.2.4.1.3"><p id="p1641533755612"><a name="p1641533755612"></a><a name="p1641533755612"></a>描述</p> 87</th> 88</tr> 89</thead> 90<tbody><tr id="row0415737175610"><td class="cellrowborder" rowspan="3" valign="top" width="12.85128512851285%" headers="mcps1.2.4.1.1 "><p id="p8866127195914"><a name="p8866127195914"></a><a name="p8866127195914"></a>创建/删除信号量</p> 91</td> 92<td class="cellrowborder" valign="top" width="29.8029802980298%" headers="mcps1.2.4.1.2 "><p id="p58621910185914"><a name="p58621910185914"></a><a name="p58621910185914"></a>LOS_SemCreate</p> 93</td> 94<td class="cellrowborder" valign="top" width="57.34573457345735%" headers="mcps1.2.4.1.3 "><p id="p48623102592"><a name="p48623102592"></a><a name="p48623102592"></a>创建信号量,返回信号量ID</p> 95</td> 96</tr> 97<tr id="row1213865218584"><td class="cellrowborder" valign="top" headers="mcps1.2.4.1.1 "><p id="p20862510115911"><a name="p20862510115911"></a><a name="p20862510115911"></a>LOS_BinarySemCreate</p> 98</td> 99<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p id="p1886211011599"><a name="p1886211011599"></a><a name="p1886211011599"></a>创建二值信号量,其计数值最大为1</p> 100</td> 101</tr> 102<tr id="row3231257145813"><td class="cellrowborder" valign="top" headers="mcps1.2.4.1.1 "><p id="p38621410205919"><a name="p38621410205919"></a><a name="p38621410205919"></a>LOS_SemDelete</p> 103</td> 104<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p id="p586261085913"><a name="p586261085913"></a><a name="p586261085913"></a>删除指定的信号量</p> 105</td> 106</tr> 107<tr id="row73651459105815"><td class="cellrowborder" rowspan="2" valign="top" width="12.85128512851285%" headers="mcps1.2.4.1.1 "><p id="p16927183515593"><a name="p16927183515593"></a><a name="p16927183515593"></a>申请/释放信号量</p> 108</td> 109<td class="cellrowborder" valign="top" width="29.8029802980298%" headers="mcps1.2.4.1.2 "><p id="p955271555916"><a name="p955271555916"></a><a name="p955271555916"></a>LOS_SemPend</p> 110</td> 111<td class="cellrowborder" valign="top" width="57.34573457345735%" headers="mcps1.2.4.1.3 "><p id="p555221518598"><a name="p555221518598"></a><a name="p555221518598"></a>申请指定的信号量,并设置超时时间</p> 112</td> 113</tr> 114<tr id="row178321454145812"><td class="cellrowborder" valign="top" headers="mcps1.2.4.1.1 "><p id="p17552101519596"><a name="p17552101519596"></a><a name="p17552101519596"></a>LOS_SemPost</p> 115</td> 116<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p id="p1555261595915"><a name="p1555261595915"></a><a name="p1555261595915"></a>释放指定的信号量</p> 117</td> 118</tr> 119</tbody> 120</table> 121 122### 开发流程<a name="section154261711141419"></a> 123 1241. 创建信号量LOS\_SemCreate,若要创建二值信号量则调用LOS\_BinarySemCreate。 1252. 申请信号量LOS\_SemPend。 1263. 释放信号量LOS\_SemPost。 1274. 删除信号量LOS\_SemDelete。 128 129> **说明:** 130>由于中断不能被阻塞,因此不能在中断中使用阻塞模式申请信号量。 131 132### 编程实例<a name="section658135571417"></a> 133 134### 实例描述<a name="section125244411653"></a> 135 136本实例实现如下功能: 137 1381. 测试任务ExampleSem创建一个信号量,锁任务调度,创建两个任务ExampleSemTask1、ExampleSemTask2, ExampleSemTask2优先级高于ExampleSemTask1,两个任务中申请同一信号量,解锁任务调度后两任务阻塞,测试任务ExampleSem释放信号量。 1392. ExampleSemTask2得到信号量,被调度,然后任务休眠20Ticks,ExampleSemTask2延迟,ExampleSemTask1被唤醒。 1403. ExampleSemTask1定时阻塞模式申请信号量,等待时间为10Ticks,因信号量仍被ExampleSemTask2持有,ExampleSemTask1挂起,10Ticks后仍未得到信号量,ExampleSemTask1被唤醒,试图以永久阻塞模式申请信号量,ExampleSemTask1挂起。 1414. 20Tick后ExampleSemTask2唤醒, 释放信号量后,ExampleSemTask1得到信号量被调度运行,最后释放信号量。 1425. ExampleSemTask1执行完,400Ticks后任务ExampleSem被唤醒,执行删除信号量。 143 144### 编程示例<a name="section1742105514512"></a> 145 146示例代码如下: 147 148``` 149#include "los_sem.h" 150#include "securec.h" 151 152/* 任务ID */ 153static UINT32 g_testTaskId01; 154static UINT32 g_testTaskId02; 155 156/* 测试任务优先级 */ 157#define TASK_PRIO_TEST 5 158 159/* 信号量结构体id */ 160static UINT32 g_semId; 161 162VOID ExampleSemTask1(VOID) 163{ 164 UINT32 ret; 165 166 printf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n"); 167 168 /* 定时阻塞模式申请信号量,定时时间为10ticks */ 169 ret = LOS_SemPend(g_semId, 10); 170 171 /* 申请到信号量 */ 172 if (ret == LOS_OK) { 173 LOS_SemPost(g_semId); 174 return; 175 } 176 /* 定时时间到,未申请到信号量 */ 177 if (ret == LOS_ERRNO_SEM_TIMEOUT) { 178 printf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n"); 179 180 /*永久阻塞模式申请信号量*/ 181 ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER); 182 printf("ExampleSemTask1 wait_forever and get sem g_semId.\n"); 183 if (ret == LOS_OK) { 184 LOS_SemPost(g_semId); 185 return; 186 } 187 } 188} 189 190VOID ExampleSemTask2(VOID) 191{ 192 UINT32 ret; 193 printf("ExampleSemTask2 try get sem g_semId wait forever.\n"); 194 195 /* 永久阻塞模式申请信号量 */ 196 ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER); 197 198 if (ret == LOS_OK) { 199 printf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n"); 200 } 201 202 /* 任务休眠20 ticks */ 203 LOS_TaskDelay(20); 204 205 printf("ExampleSemTask2 post sem g_semId.\n"); 206 /* 释放信号量 */ 207 LOS_SemPost(g_semId); 208 return; 209} 210 211UINT32 ExampleSem(VOID) 212{ 213 UINT32 ret; 214 TSK_INIT_PARAM_S task1; 215 TSK_INIT_PARAM_S task2; 216 217 /* 创建信号量 */ 218 LOS_SemCreate(0, &g_semId); 219 220 /* 锁任务调度 */ 221 LOS_TaskLock(); 222 223 /* 创建任务1 */ 224 (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); 225 task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1; 226 task1.pcName = "TestTask1"; 227 task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 228 task1.usTaskPrio = TASK_PRIO_TEST; 229 ret = LOS_TaskCreate(&g_testTaskId01, &task1); 230 if (ret != LOS_OK) { 231 printf("task1 create failed .\n"); 232 return LOS_NOK; 233 } 234 235 /* 创建任务2 */ 236 (VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); 237 task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2; 238 task2.pcName = "TestTask2"; 239 task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 240 task2.usTaskPrio = (TASK_PRIO_TEST - 1); 241 ret = LOS_TaskCreate(&g_testTaskId02, &task2); 242 if (ret != LOS_OK) { 243 printf("task2 create failed.\n"); 244 return LOS_NOK; 245 } 246 247 /* 解锁任务调度 */ 248 LOS_TaskUnlock(); 249 250 ret = LOS_SemPost(g_semId); 251 252 /* 任务休眠400 ticks */ 253 LOS_TaskDelay(400); 254 255 /* 删除信号量 */ 256 LOS_SemDelete(g_semId); 257 return LOS_OK; 258} 259``` 260 261### 结果验证<a name="section11297301617"></a> 262 263编译运行得到的结果为: 264 265``` 266ExampleSemTask2 try get sem g_semId wait forever. 267ExampleSemTask2 get sem g_semId and then delay 20 ticks. 268ExampleSemTask1 try get sem g_semId, timeout 10 ticks. 269ExampleSemTask1 timeout and try get sem g_semId wait forever. 270ExampleSemTask2 post sem g_semId. 271ExampleSemTask1 wait_forever and get sem g_semId. 272``` 273 274