# 小凌派-RK2206开发板OpenHarmonyOS内核开发-信号量
## 实验内容
本例程演示如何在小凌派-RK2206开发板上使用鸿蒙LiteOS-M内核接口,通过信号量控制不同的线程,实现任务之间的同步。

## 程序设计
信号量是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。
一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况:
* 0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。
* 正值,表示该信号量当前可被获取。
信号量是一种上锁机制,代码必须获得对应的钥匙才能继续执行。一旦获得了钥匙,就意味着该任务具有进入被锁部分代码的权限。一旦执行被锁代码段,则其它任务需要等待,直到被锁部分代码的钥匙被再次释放才能继续执行。任务可以利用信号量与其它任务、中断服务程序进行同步。
本例程设计3个任务,任务control_thread每隔1秒释放信号量,任务sem_one_thread和sem_two_thread则不断地尝试获取信号量。
### API分析
**头文件:**
/kernel/liteos_m/kernel/include/los_sem.h
#### LOS_SemCreate()
```c
UINT32 LOS_SemCreate(UINT16 count, UINT32 *semHandle);
```
**描述:**
创建信号量,返回信号量ID
**参数:**
| 名字 | 描述 |
| :-------- | :--------------- |
| count | 可用的信号量数量 |
| semHandle | 申请的信号量指针 |
**返回值:**
| 返回值 | 描述 |
| :------------------------------------------------------------------------------------- | :--- |
| LOS_OK | 成功 |
| LOS_ERRNO_SEM_PTR_NULL`
` LOS_ERRNO_SEM_OVERFLOW `
` LOS_ERRNO_SEM_ALL_BUSY | 失败 |
#### LOS_SemDelete()
```c
UINT32 LOS_SemDelete(UINT32 semHandle);
```
**描述:**
删除指定的信号量
**参数:**
| 名字 | 描述 |
| :-------- | :--------------- |
| semHandle | 需要删除的信号量 |
**返回值:**
| 返回值 | 描述 |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--- |
| LOS_OK | 成功 |
| LOS_ERRNO_SEM_INVALID`
` LOS_ERRNO_SEM_UNAVAILABLE `
` LOS_ERRNO_SEM_PEND_INTERR `
` LOS_ERRNO_SEM_PEND_IN_LOCK `
` LOS_ERRNO_SEM_TIMEOUT | 失败 |
#### LOS_SemPend()
```c
UINT32 LOS_SemPend(UINT32 semHandle, UINT32 timeout);
```
**描述:**
申请指定的信号量,并设置超时时间
**参数:**
| 名字 | 描述 |
| :-------- | :--------------- |
| semHandle | 需要申请的信号量 |
| timeout | 超时时间 |
**返回值:**
| 返回值 | 描述 |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--- |
| LOS_OK | 成功 |
| LOS_ERRNO_SEM_INVALID`
` LOS_ERRNO_SEM_UNAVAILABLE `
` LOS_ERRNO_SEM_PEND_INTERR `
` LOS_ERRNO_SEM_PEND_IN_LOCK `
` LOS_ERRNO_SEM_TIMEOUT | 失败 |
#### LOS_SemPost()
```c
UINT32 LOS_SemPost(UINT32 semHandle);
```
**描述:**
释放指定的信号量
**参数:**
| 名字 | 描述 |
| :-------- | :--------------- |
| semHandle | 需要释放的信号量 |
**返回值:**
| 返回值 | 描述 |
| :--------------------------------------------------- | :--- |
| LOS_OK | 成功 |
| LOS_ERRNO_SEM_INVALID`
` LOS_ERRNO_SEM_OVERFLOW | 失败 |
### 软件设计
**主要代码分析**
在semaphore_example函数中,通过LOS_SemCreate函数创建m_sem信号量,并通过LOS_TaskCreate函数创建三个线程:control_thread、sem_one_thread和sem_two_thread。
```c
void semaphore_example()
{
unsigned int thread_crtl;
unsigned int thread_id1;
unsigned int thread_id2;
TSK_INIT_PARAM_S task1 = {0};
TSK_INIT_PARAM_S task2 = {0};
TSK_INIT_PARAM_S task3 = {0};
unsigned int ret = LOS_OK;
ret = LOS_SemCreate(MAX_COUNT, &m_sem);
if (ret != LOS_OK)
{
printf("Failed to create Semaphore\n");
return;
}
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)control_thread;
task1.uwStackSize = 2048;
task1.pcName = "control_thread";
task1.usTaskPrio = 24;
ret = LOS_TaskCreate(&thread_crtl, &task1);
if (ret != LOS_OK)
{
printf("Failed to create control_thread ret:0x%x\n", ret);
return;
}
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)sem_one_thread;
task2.uwStackSize = 2048;
task2.pcName = "sem_one_thread";
task2.usTaskPrio = 24;
ret = LOS_TaskCreate(&thread_id1, &task2);
if (ret != LOS_OK)
{
printf("Failed to create sem_one_thread ret:0x%x\n", ret);
return;
}
task3.pfnTaskEntry = (TSK_ENTRY_FUNC)sem_two_thread;
task3.uwStackSize = 2048;
task3.pcName = "sem_two_thread";
task3.usTaskPrio = 24;
ret = LOS_TaskCreate(&thread_id2, &task3);
if (ret != LOS_OK)
{
printf("Failed to create sem_two_thread ret:0x%x\n", ret);
return;
}
}
```
control_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轮流执行。
```c
void control_thread()
{
unsigned int count = 0;
while (1)
{
/*释放两次信号量,sem_one_thread和sem_two_thread同步执行;
释放一次信号量,sem_one_thread和sem_two_thread交替执行*/
if (count++%3)
{
LOS_SemPost(m_sem);
printf("control_thread Release once Semaphore\n");
}
else
{
LOS_SemPost(m_sem);
LOS_SemPost(m_sem);
printf("control_thread Release twice Semaphore\n");
}
LOS_Msleep(1000);
}
}
void sem_one_thread()
{
while (1)
{
/*申请信号量*/
LOS_SemPend(m_sem, LOS_WAIT_FOREVER);
printf("sem_one_thread get Semaphore\n");
LOS_Msleep(100);
}
}
void sem_two_thread()
{
while (1)
{
/*申请信号量*/
LOS_SemPend(m_sem, LOS_WAIT_FOREVER);
printf("sem_two_thread get Semaphore\n");
LOS_Msleep(100);
}
}
```
## 编译调试
### 修改 BUILD.gn 文件
修改 `vendor/lockzhiner/lingpi/sample` 路径下 BUILD.gn 文件,指定 `a2_kernel_semaphore` 参与编译。
```r
"a2_kernel_semaphore",
```
在主目录下输入编译命令。
```shell
hb build -f
```
### 运行结果
例程代码编译烧写到开发板后,按下开发板的RESET按键,通过串口软件查看日志,control_thread一次释放两个信号量,sem_one_thread和sem_two_thread同步执行;control_thread一次释放一个信号量,sem_one_thread和sem_two_thread交替执行。
```r
control_thread Release once Semaphore
sem_one_thread get Semaphore
control_thread Release once Semaphore
sem_two_thread get Semaphore
control_thread Release twice Semaphore
sem_two_thread get Semaphore
sem_one_thread get Semaphore
```