1# 小凌派-RK2206开发板OpenHarmonyOS内核开发-信号量 2 3## 实验内容 4 5本例程演示如何在小凌派-RK2206开发板上使用鸿蒙LiteOS-M内核接口,通过信号量控制不同的线程,实现任务之间的同步。 6 7![小凌派-RK2206开发板](../../docs/figures/lockzhiner-rk2206.jpg) 8 9## 程序设计 10 11信号量是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。 12 13一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况: 14 15* 0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。 16* 正值,表示该信号量当前可被获取。 17 18信号量是一种上锁机制,代码必须获得对应的钥匙才能继续执行。一旦获得了钥匙,就意味着该任务具有进入被锁部分代码的权限。一旦执行被锁代码段,则其它任务需要等待,直到被锁部分代码的钥匙被再次释放才能继续执行。任务可以利用信号量与其它任务、中断服务程序进行同步。 19 20本例程设计3个任务,任务control_thread每隔1秒释放信号量,任务sem_one_thread和sem_two_thread则不断地尝试获取信号量。 21 22### API分析 23 24**头文件:** 25 26/kernel/liteos_m/kernel/include/los_sem.h 27 28#### LOS_SemCreate() 29 30```c 31UINT32 LOS_SemCreate(UINT16 count, UINT32 *semHandle); 32``` 33 34**描述:** 35 36创建信号量,返回信号量ID 37 38**参数:** 39 40| 名字 | 描述 | 41| :-------- | :--------------- | 42| count | 可用的信号量数量 | 43| semHandle | 申请的信号量指针 | 44 45**返回值:** 46 47| 返回值 | 描述 | 48| :------------------------------------------------------------------------------------- | :--- | 49| LOS_OK | 成功 | 50| LOS_ERRNO_SEM_PTR_NULL`<br>` LOS_ERRNO_SEM_OVERFLOW `<br>` LOS_ERRNO_SEM_ALL_BUSY | 失败 | 51 52#### LOS_SemDelete() 53 54```c 55UINT32 LOS_SemDelete(UINT32 semHandle); 56``` 57 58**描述:** 59 60删除指定的信号量 61 62**参数:** 63 64| 名字 | 描述 | 65| :-------- | :--------------- | 66| semHandle | 需要删除的信号量 | 67 68**返回值:** 69 70| 返回值 | 描述 | 71| :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--- | 72| LOS_OK | 成功 | 73| LOS_ERRNO_SEM_INVALID`<br>` LOS_ERRNO_SEM_UNAVAILABLE `<br>` LOS_ERRNO_SEM_PEND_INTERR `<br>` LOS_ERRNO_SEM_PEND_IN_LOCK `<br>` LOS_ERRNO_SEM_TIMEOUT | 失败 | 74 75#### LOS_SemPend() 76 77```c 78UINT32 LOS_SemPend(UINT32 semHandle, UINT32 timeout); 79``` 80 81**描述:** 82申请指定的信号量,并设置超时时间 83 84**参数:** 85 86| 名字 | 描述 | 87| :-------- | :--------------- | 88| semHandle | 需要申请的信号量 | 89| timeout | 超时时间 | 90 91**返回值:** 92 93| 返回值 | 描述 | 94| :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--- | 95| LOS_OK | 成功 | 96| LOS_ERRNO_SEM_INVALID`<br>` LOS_ERRNO_SEM_UNAVAILABLE `<br>` LOS_ERRNO_SEM_PEND_INTERR `<br>` LOS_ERRNO_SEM_PEND_IN_LOCK `<br>` LOS_ERRNO_SEM_TIMEOUT | 失败 | 97 98#### LOS_SemPost() 99 100```c 101UINT32 LOS_SemPost(UINT32 semHandle); 102``` 103 104**描述:** 105释放指定的信号量 106 107**参数:** 108 109| 名字 | 描述 | 110| :-------- | :--------------- | 111| semHandle | 需要释放的信号量 | 112 113**返回值:** 114 115| 返回值 | 描述 | 116| :--------------------------------------------------- | :--- | 117| LOS_OK | 成功 | 118| LOS_ERRNO_SEM_INVALID`<br>` LOS_ERRNO_SEM_OVERFLOW | 失败 | 119 120### 软件设计 121 122**主要代码分析** 123 124在semaphore_example函数中,通过LOS_SemCreate函数创建m_sem信号量,并通过LOS_TaskCreate函数创建三个线程:control_thread、sem_one_thread和sem_two_thread。 125 126```c 127void semaphore_example() 128{ 129 unsigned int thread_crtl; 130 unsigned int thread_id1; 131 unsigned int thread_id2; 132 TSK_INIT_PARAM_S task1 = {0}; 133 TSK_INIT_PARAM_S task2 = {0}; 134 TSK_INIT_PARAM_S task3 = {0}; 135 unsigned int ret = LOS_OK; 136 137 ret = LOS_SemCreate(MAX_COUNT, &m_sem); 138 if (ret != LOS_OK) 139 { 140 printf("Failed to create Semaphore\n"); 141 return; 142 } 143 144 task1.pfnTaskEntry = (TSK_ENTRY_FUNC)control_thread; 145 task1.uwStackSize = 2048; 146 task1.pcName = "control_thread"; 147 task1.usTaskPrio = 24; 148 ret = LOS_TaskCreate(&thread_crtl, &task1); 149 if (ret != LOS_OK) 150 { 151 printf("Failed to create control_thread ret:0x%x\n", ret); 152 return; 153 } 154 155 task2.pfnTaskEntry = (TSK_ENTRY_FUNC)sem_one_thread; 156 task2.uwStackSize = 2048; 157 task2.pcName = "sem_one_thread"; 158 task2.usTaskPrio = 24; 159 ret = LOS_TaskCreate(&thread_id1, &task2); 160 if (ret != LOS_OK) 161 { 162 printf("Failed to create sem_one_thread ret:0x%x\n", ret); 163 return; 164 } 165 166 task3.pfnTaskEntry = (TSK_ENTRY_FUNC)sem_two_thread; 167 task3.uwStackSize = 2048; 168 task3.pcName = "sem_two_thread"; 169 task3.usTaskPrio = 24; 170 ret = LOS_TaskCreate(&thread_id2, &task3); 171 if (ret != LOS_OK) 172 { 173 printf("Failed to create sem_two_thread ret:0x%x\n", ret); 174 return; 175 } 176} 177``` 178 179control_thread函数中通过LOS_SemPost函数释放信号量,sem_one_thread和sem_two_thread函数中,则阻塞等待sem信号量。当Thread_Control函数中释放两次信号量,sem_one_thread和sem_two_thread同步运行;当control_thread函数只释放一次信号量,sem_one_thread和sem_two_thread轮流执行。 180 181```c 182void control_thread() 183{ 184 unsigned int count = 0; 185 186 while (1) 187 { 188 /*释放两次信号量,sem_one_thread和sem_two_thread同步执行; 189 释放一次信号量,sem_one_thread和sem_two_thread交替执行*/ 190 if (count++%3) 191 { 192 LOS_SemPost(m_sem); 193 printf("control_thread Release once Semaphore\n"); 194 } 195 else 196 { 197 LOS_SemPost(m_sem); 198 LOS_SemPost(m_sem); 199 printf("control_thread Release twice Semaphore\n"); 200 } 201 202 LOS_Msleep(1000); 203 } 204} 205 206void sem_one_thread() 207{ 208 while (1) 209 { 210 /*申请信号量*/ 211 LOS_SemPend(m_sem, LOS_WAIT_FOREVER); 212 213 printf("sem_one_thread get Semaphore\n"); 214 LOS_Msleep(100); 215 } 216} 217 218void sem_two_thread() 219{ 220 while (1) 221 { 222 /*申请信号量*/ 223 LOS_SemPend(m_sem, LOS_WAIT_FOREVER); 224 225 printf("sem_two_thread get Semaphore\n"); 226 LOS_Msleep(100); 227 } 228} 229``` 230 231## 编译调试 232 233### 修改 BUILD.gn 文件 234 235修改 `vendor/lockzhiner/lingpi/sample` 路径下 BUILD.gn 文件,指定 `a2_kernel_semaphore` 参与编译。 236 237```r 238"a2_kernel_semaphore", 239``` 240 241在主目录下输入编译命令。 242 243```shell 244hb build -f 245``` 246 247### 运行结果 248 249例程代码编译烧写到开发板后,按下开发板的RESET按键,通过串口软件查看日志,control_thread一次释放两个信号量,sem_one_thread和sem_two_thread同步执行;control_thread一次释放一个信号量,sem_one_thread和sem_two_thread交替执行。 250 251```r 252control_thread Release once Semaphore 253sem_one_thread get Semaphore 254control_thread Release once Semaphore 255sem_two_thread get Semaphore 256control_thread Release twice Semaphore 257sem_two_thread get Semaphore 258sem_one_thread get Semaphore 259``` 260