简单理解:FreeRTOS 中,信号量(Semaphore)函数及其作用
在 FreeRTOS 中,信号量(Semaphore) 是一种用于任务间同步或资源计数的机制,本质上是一个受保护的整数,通过对其进行 “获取”(减 1)和 “释放”(加 1)操作实现任务间的协调。信号量主要分为二进制信号量(值为 0 或 1)和计数信号量(值为 0 到最大计数),适用于不同场景。
信号量的作用
任务同步实现任务间的事件通知(如 “生产者 - 消费者” 模型):一个任务完成操作后释放信号量,唤醒等待该信号量的另一个任务。例如,传感器数据就绪后,中断服务程序(ISR)释放信号量,唤醒处理数据的任务。
资源计数限制同时访问某资源的任务数量(计数信号量)。例如,限制最多 3 个任务同时使用某个硬件外设,信号量初始值为 3,每个任务获取信号量(减 1)后使用资源,释放时加 1。
事件标志作为事件发生的标志,任务通过等待信号量判断事件是否发生(二进制信号量常用此场景)。
常用信号量函数
1. 创建信号量
(1)二进制信号量(初始值 0 或 1)
SemaphoreHandle_t xSemaphoreCreateBinary(void);
- 功能:创建二进制信号量,初始值为 0(需先调用
xSemaphoreGive使其变为 1)。 - 若需初始值为 1(类似互斥锁初始状态),可使用:
(静态创建,需手动初始化值为 1)。SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer);
(2)计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, // 最大计数(信号量允许的最大值)UBaseType_t uxInitialCount // 初始计数
);
- 例如:
xSemaphoreCreateCounting(5, 3)表示最大计数 5,初始值 3。
2. 获取信号量(阻塞方式)
任务尝试获取信号量(信号量值减 1),若值为 0 则阻塞等待。
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, // 信号量句柄TickType_t xTicksToWait // 阻塞等待时间(tick数,0表示不等待,portMAX_DELAY表示永久等待)
);
- 返回值:
pdPASS成功(信号量值≥1,减 1 后返回);errQUEUE_EMPTY超时失败(信号量始终为 0)。
3. 释放信号量(任务中使用)
任务释放信号量(信号量值加 1),唤醒等待该信号量的任务。
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore // 信号量句柄
);
- 返回值:
pdPASS成功(信号量值未达最大值,加 1 后返回);errQUEUE_FULL失败(计数信号量已达uxMaxCount)。
4. 中断中释放信号量
在中断服务程序(ISR)中释放信号量(不可阻塞)。
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken // 输出参数:若为pdTRUE,需触发任务切换
);
- 功能:中断安全版本的释放函数,无阻塞时间,适合在 ISR 中通知任务(如数据就绪)。
5. 删除信号量
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
- 功能:释放信号量占用的内存,删除后句柄失效。
注意事项
二进制信号量 vs 计数信号量
- 二进制信号量:值只能是 0 或 1,适合同步(如事件通知),类似 “开关”。
- 计数信号量:值可在 0 到
uxMaxCount之间,适合资源计数(如限制并发访问数量)。
与互斥锁的区别
- 信号量无 “优先级继承” 机制,不适合保护共享资源(可能导致优先级反转)。
- 互斥锁是特殊的二进制信号量,带优先级继承,专门用于资源保护;普通信号量更适合同步。
中断中的使用限制
- 中断中只能使用
xSemaphoreGiveFromISR释放信号量,不能调用xSemaphoreTake(因可能阻塞,中断需快速执行)。
- 中断中只能使用
信号量的所有权信号量可以由任意任务或中断释放(无需与获取者一致),而互斥锁必须由获取它的任务释放。
信号量是 FreeRTOS 中灵活的同步工具,二进制信号量适合任务间事件通知,计数信号量适合资源并发控制,需根据场景选择使用。
