以生活中的停车场为例来理解信号量的概念:
在此例子中
信号量的工作机制
信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。

在 RT-Thread 中,信号量控制块是操作系统用于管理信号量的一个数据结构
rtos\rt-thread\include\rtdef.h
#ifdef RT_USING_SEMAPHORE
/**
* Semaphore(**信号量**) structure
*
* **信号量控制块**
*/
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit(**继承**) from ipc_object(**进[线]程间通信对象**) */
rt_uint16_t value; /**< **信号量的值** , 它是uint16_t类型的,所以它的**最大值**为**65535***/
rt_uint16_t reserved; /**< 保留字段 */
};
typedef struct rt_semaphore *rt_sem_t;
#endif
// **定义静态信号量**
struct rt_semaphore static_sem; **// 结构体类型**
// **定义动态信号量 (后续需要我们自己去给它分配内存空间)**
rt_sem_t dynamic_sem; **// 指针类型**
定义好信号量之后,就需要对信号量进行相应的操作,RT-Thread中提供了一整套对信号量进行操作的API
信号量的操作
初始化与脱离(针对于静态信号量)
// **决定了信号量的值为0的时候,多个线程等待信号量的排列方式
// |**
// **静态信号量的初始化 信号量指针 信号量名称 信号量初始值 信号量标志位————————————> RT_IPC_FLAG_FIFO—————先进先出,先进入线程等待队列的线程将优先获得信号量**
rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag); // **|———>** **RT_IPC_FLAG_PRIO—————线程将按照优先级的方式排队等候信号量,优先级高的线程将优先获得信号量**
**// 通过这个函数,可以将静态信号量,加入,系统的,对象管理器**
// **静态信号量的脱离 信号量指针**
rt_err_t rt_sem_detach(rt_sem_t sem); // **不再使用**这个**静态信号量**的**时**候,用于**将静态信号量从系统的,对象管理器,中移除,释放系统资源**
创建与删除(针对于动态信号量)
// **动态信号量的创建 信号量名称 信号量初始值 信号量标志位————> 1.RT_IPC_FLAG_FIFO** 2.**RT_IPC_FLAG_PRIO**
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); // 需要**动态申请**信号量所占的**内存空间**
// **创建成功之后**,得到 rt_sem_t dynamic_sem 的**指针**,**需要进行判断**一下,如果 dynamic_sem **不为 NULL**,那么说明**创建成功**了,那么接下来就可以**对新创建的**这个**动态信号量**进行**操作**
// **动态信号量的删除 信号量指针**
rt_err_t rt_sem_delete(rt_sem_t sem); // **不再使用**这个**动态信号量**的**时**候,用于**将动态信号量从系统的,对象管理器,中移除,释放系统资源**
获取信号量
注意:当获取不到信号量的时候,这两个函数会导致线程被挂起,所以这个API只能在线程中去调用,不能在中断(ISR)中去调用
但中断上下文不允许阻塞,因为它没有关联的线程上下文,阻塞会导致系统未定义行为(如死锁或崩溃)
// 表示**要获取的信号量**
// |
// **信号量的获取 信号量指针 时间参数 RT_WAITING_FOREVER = -1**
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) // **线程**通过**这个API**来**获得信号量**所**指示的共享资源**,当**信号量的值**(sem.value)**大于0**的时候,线程将**成功**的**获得信号量**
// 获得信号量之后,这个函数将立即返回RT_EOK,并且将**信号量的值**(sem.value)**减一**
// 当线程调用这个函数获取信号量的时候,如果**信号量的值**(sem.value)**等于0**,说明信号量所指示的**共享资源不可用**
// 申请该信号量的线程会根据**时间参数**(**time**)来进行相应的动作
// 当**时间参数**(**time**)大于**0**的时候,会按照时间来进行等待,等到时间了之后会返回
// 当**时间参数**(**time**)等于**0**的时候,会立即返回
// **时间参数**(**time**)是以**系统**的**滴嗒时钟**为**计数单位**的
// 如果**时间参数**(**time**)为负数,那么表示**这个线程**会**永远的**在这个函数里面**等待获取信号量** (负数一般会用 **RT_WAITING_FOREVER = -1** 来定义)
非阻塞获取信号量(可以在中断中使用)
// **信号量的尝试获取**
rt_err_t rt_sem_trytake(rt_sem_t sem); // 是一个**时间参数**(**time**)为0的rt_sem_take,不做任何等待,只是看是否可以获取到,如果可以获取到会进行返回RT_EOK,如果获取不到会返回-RT_ETIMEOUT
释放信号量
既可以在线程中去释放,也可以在中断(ISR)中去释放
非阻塞的原子操作**,通过关中断保护临界区**
// **信号量的释放 信号量指针**
rt_err_t rt_sem_release(rt_sem_t sem); // 调用这个函数,可以将信号量所指示的共享资源的数量(**sem.value**)**加一**
// 如果**有线程被**这个信号量**挂起**了,那么这个时候**挂在这个信号量下面**的这个**线程**就会被**唤醒**
应用示例