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