【FreeRTOS】(号外)任务间通讯2: 信号量- Counting Semaphore
计数信号量是 FreeRTOS 提供的一种关键同步机制,其核心设计初衷是管理有限资源的高效分配与安全共享。与二值信号量(仅表示“有/无”状态)不同,计数信号量允许资源实例被多次分配和释放,适用于以下典型场景:
1. 核心特性与设计意图
资源池管理:
当系统存在多个相同资源的实例(如内存块、外设句柄、网络连接池等),计数信号量通过计数器动态跟踪可用资源数量:xSemaphoreCreateCounting(maxCount, initialCount)
:
初始化时设定资源池的最大容量(maxCount
)和初始可用数量(initialCount
)。xSemaphoreTake()
:
申请资源,计数器递减;若计数器为 0,则任务阻塞(或立即返回,取决于阻塞时间设置)。xSemaphoreGive()
:
释放资源,计数器递增,唤醒等待任务。
线程安全的计数器:
其底层通过原子操作确保计数器的增减是线程安全的,避免多任务竞争导致的数据错误。
2. 典型应用场景
有限资源池:
例如管理 10 个可用的 UART 发送缓冲区,任务通过信号量申请/释放缓冲区。SemaphoreHandle_t xBufferSem = xSemaphoreCreateCounting(10, 10); // 初始化10个可用缓冲区void vTaskSender(void *pvParams) {while (1) {if (xSemaphoreTake(xBufferSem, portMAX_DELAY) { // 申请缓冲区send_data_to_uart(); // 使用资源xSemaphoreGive(xBufferSem); // 释放资源}} }
事件计数:
统计异步事件的发生次数(如传感器数据到达次数),任务通过信号量感知事件累积量。SemaphoreHandle_t xEventSem = xSemaphoreCreateCounting(100, 0); // 初始无事件// 中断服务例程(ISR)中触发事件 void vSensorISR() {BaseType_t xHigherPriorityTaskWoken = pdFALSE;xSemaphoreGiveFromISR(xEventSem, &xHigherPriorityTaskWoken); // 事件计数+1portYIELD_FROM_ISR(xHigherPriorityTaskWoken); }
流量控制:
限制同时执行某操作的并发任务数(如最多 3 个任务同时访问 SD 卡)。
3. 与二值信号量的关键区别
特性 | 计数信号量 | 二值信号量 |
---|---|---|
资源表示 | 多实例(计数器 ≥ 0) | 单实例(0 或 1) |
阻塞行为 | 计数器为 0 时任务阻塞 | 信号量为 0 时任务阻塞 |
适用场景 | 资源池、事件计数、并发控制 | 任务同步、互斥锁(需配合互斥量) |
4. 使用注意事项
初始化合理性:
initialCount
必须 ≤maxCount
,否则可能导致逻辑错误。优先级反转风险:
若高优先级任务因信号量阻塞,可能被中优先级任务抢占,需结合优先级继承机制(如改用互斥量)。ISR 中的操作:
必须使用xSemaphoreGiveFromISR()
或xSemaphoreTakeFromISR()
,避免在中断中阻塞。
5. 总结
计数信号量是 FreeRTOS 中管理多实例资源的首选工具,其通过原子化计数器实现了资源的安全分配、高效回收和任务同步。正确使用该机制可显著提升系统可靠性,尤其在资源受限的嵌入式环境中。