• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![](figure/信号量运作示意图-22.png "信号量运作示意图-22")
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>![](../public_sys-resources/icon-note.gif) **说明:**
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