• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Semaphore
2
3
4## Basic Concepts
5
6Semaphore is a mechanism used to implement synchronization between tasks or exclusive access to shared resources.
7
8In the semaphore data structure, there is a value indicating the number of shared resources available. The value can be:
9
10- **0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist.
11
12- Positive number: The semaphore is available.
13
14The semaphore used for exclusive access to resources is different from the semaphore used for synchronization:
15
16-   Semaphore used for exclusive access: The initial semaphore counter value \(non-zero\) indicates the number of shared resources available. A semaphore must be acquired before a shared resource is used, and released when the resource is no longer required. When all shared resources are used, the semaphore counter is reduced to 0 and all tasks requiring the semaphore will be blocked. This ensures exclusive access to shared resources. In addition, if the number of shared resources is 1, a binary semaphore \(similar to the mutex mechanism\) is recommended.
17
18-   Semaphore used for synchronization: The initial semaphore counter value is  **0**. A task without the semaphore will be blocked, and enters the Ready or Running state only when the semaphore is released by another task or an interrupt.
19
20
21## Working Principles
22
23**Semaphore Control Block**
24
25
26```
27/**
28  * Data structure of the semaphore control block
29 */
30typedef struct {
31    UINT16            semStat;          /* Semaphore status */
32    UINT16            semType;          /* Semaphore type */
33    UINT16            semCount;         /* Semaphore count */
34    UINT16            semId;            /* Semaphore ID */
35    LOS_DL_LIST       semList;          /* List of blocked tasks */
36} LosSemCB;
37```
38
39**Working Principles**
40
41Semaphore allows only a specified number of tasks to access a shared resource at a time. When the number of tasks accessing the resource reaches the limit, other tasks will be blocked until the semaphore is released.
42
43- Semaphore initialization
44
45  Allocate memory for the semaphores (the number of semaphores is specified by the **LOSCFG_BASE_IPC_SEM_LIMIT** macro), set all semaphores to the unused state, and add them to a linked list.
46
47- Semaphore creation
48
49  Obtain a semaphore from the linked list of unused semaphores and assign an initial value to the semaphore.
50
51- Semaphore request
52
53  If the counter value is greater than 0 when a semaphore is requsted, the counter is decreased by 1 and a success message is returned. Otherwise, the task is blocked and added to the end of a task queue waiting for semaphores. The wait timeout period can be set.
54
55- Semaphore release
56
57  If no task is waiting for the semaphore, the counter is incremented by 1. Otherwise, wake up the first task in the wait queue.
58
59- Semaphore deletion
60
61  Set a semaphore in use to the unused state and add it to the linked list of unused semaphores.
62
63The following figure illustrates the semaphore working mechanism.
64
65**Figure 1** Semaphore working mechanism for the small system
66
67![](figures/semaphore-working-mechanism-for-small-systems.png "semaphore-working-mechanism-for-small-systems")
68
69
70## Development Guidelines
71
72
73### Available APIs
74
75**Table 1** APIs for creating and deleting a semaphore
76
77| API| Description|
78| -------- | -------- |
79| LOS_SemCreate | Creates a semaphore and returns the semaphore ID.|
80| LOS_BinarySemCreate | Creates a binary semaphore. The maximum counter value is **1**.|
81| LOS_SemDelete | Deletes a semaphore.|
82
83**Table 2** APIs for requesting and releasing a semaphore
84
85| API| Description|
86| -------- | -------- |
87| LOS_SemPend | Requests a semaphore and sets a timeout period.|
88| LOS_SemPost | Releases a semaphore.|
89
90
91### How to Develop
92
931. Call **LOS_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS_BinarySemCreate**.
94
952. Call **LOS_SemPend** to request a semaphore.
96
973. Call **LOS_SemPost** to release a semaphore.
98
994. Call **LOS_SemDelete** to delete a semaphore.
100
101> **NOTE**<br>
102> As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts.
103
104
105### Development Example
106
107
108### Example Description
109
110This example implements the following:
111
1121. Create a semaphore in task **ExampleSem** and lock task scheduling. Create two tasks **ExampleSemTask1** and **ExampleSemTask2** (with higher priority). Enable the two tasks to request the same semaphore. Unlock task scheduling. Enable task **ExampleSem** to enter sleep mode for 400 ticks. Release the semaphore in task **ExampleSem**.
113
1142. Enable **ExampleSemTask2** to enter sleep mode for 20 ticks after acquiring the semaphore. (When **ExampleSemTask2** is delayed, **ExampleSemTask1** is woken up.)
115
1163. Enable **ExampleSemTask1** to request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. (Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended. **ExampleSemTask1** is woken up after 10 ticks.) Enable **ExampleSemTask1** to request the semaphore in permanent block mode after it is woken up 10 ticks later. (Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended.)
117
1184. After 20 ticks, **ExampleSemTask2** is woken up and releases the semaphore. **ExampleSemTask1** acquires the semaphore and is scheduled to run. When **ExampleSemTask1** is complete, it releases the semaphore.
119
1205. Task **ExampleSem** is woken up after 400 ticks. After that, delete the semaphore.
121
122
123### Sample Code
124
125The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **ExampleSem** function is called in **TestTaskEntry**.
126
127The sample code is as follows:
128
129```
130#include "los_sem.h"
131#include "securec.h"
132
133/* Task ID*/
134static UINT32 g_testTaskId01;
135static UINT32 g_testTaskId02;
136
137/* Task priority */
138#define TASK_PRIO_LOW   5
139#define TASK_PRIO_HI    4
140
141/* Semaphore structure ID */
142static UINT32 g_semId;
143
144VOID ExampleSemTask1(VOID)
145{
146    UINT32 ret;
147
148    dprintf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n");
149
150    /* Request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. */
151    ret = LOS_SemPend(g_semId, 10);
152    /* The semaphore is acquired. */
153    if (ret == LOS_OK) {
154         LOS_SemPost(g_semId);
155         return;
156    }
157    /* The semaphore is not acquired when the timeout period has expired. */
158    if (ret == LOS_ERRNO_SEM_TIMEOUT) {
159        dprintf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n");
160
161        /* Request the semaphore in permanent block mode. */
162        ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
163        dprintf("ExampleSemTask1 wait_forever and get sem g_semId.\n");
164        if (ret == LOS_OK) {
165            dprintf("ExampleSemTask1 post sem g_semId.\n");
166            LOS_SemPost(g_semId);
167            return;
168        }
169    }
170}
171
172VOID ExampleSemTask2(VOID)
173{
174    UINT32 ret;
175    dprintf("ExampleSemTask2 try get sem g_semId wait forever.\n");
176
177    /* Request the semaphore in permanent block mode. */
178    ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
179    if (ret == LOS_OK) {
180        dprintf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n");
181    }
182
183    /* Enable the task to enter sleep mode for 20 ticks. */
184    LOS_TaskDelay(20);
185
186    dprintf("ExampleSemTask2 post sem g_semId.\n");
187    /* Release the semaphore. */
188    LOS_SemPost(g_semId);
189    return;
190}
191
192UINT32 ExampleSem(VOID)
193{
194    UINT32 ret;
195    TSK_INIT_PARAM_S task1;
196    TSK_INIT_PARAM_S task2;
197
198   /* Create a semaphore. */
199    LOS_SemCreate(0, &g_semId);
200
201    /* Lock task scheduling. */
202    LOS_TaskLock();
203
204    /* Create task 1. */
205    (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
206    task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1;
207    task1.pcName       = "TestTask1";
208    task1.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
209    task1.usTaskPrio   = TASK_PRIO_LOW;
210    ret = LOS_TaskCreate(&g_testTaskId01, &task1);
211    if (ret != LOS_OK) {
212        dprintf("task1 create failed .\n");
213        return LOS_NOK;
214    }
215
216    /* Create task 2. */
217    (VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
218    task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2;
219    task2.pcName       = "TestTask2";
220    task2.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
221    task2.usTaskPrio   = TASK_PRIO_HI;
222    ret = LOS_TaskCreate(&g_testTaskId02, &task2);
223    if (ret != LOS_OK) {
224        dprintf("task2 create failed.\n");
225        return LOS_NOK;
226    }
227
228    /* Unlock task scheduling. */
229    LOS_TaskUnlock();
230
231     /* Enable the task to enter sleep mode for 400 ticks. */
232    LOS_TaskDelay(400);
233
234    ret = LOS_SemPost(g_semId);
235
236     /* Enable the task to enter sleep mode for 400 ticks. */
237    LOS_TaskDelay(400);
238
239    /* Delete the semaphore. */
240    LOS_SemDelete(g_semId);
241    return LOS_OK;
242}
243```
244
245
246### Verification
247
248The development is successful if the return result is as follows:
249
250
251```
252ExampleSemTask2 try get sem g_semId wait forever.
253ExampleSemTask1 try get sem g_semId, timeout 10 ticks.
254ExampleSemTask1 timeout and try get sem g_semId wait forever.
255ExampleSemTask2 get sem g_semId and then delay 20 ticks.
256ExampleSemTask2 post sem g_semId.
257ExampleSemTask1 wait_forever and get sem g_semId.
258ExampleSemTask1 post sem g_semId.
259```
260