FreeRTOS系列第24篇—FreeRTOS信号量分析

💡 原文中文,约12000字,阅读约需29分钟。
📝

内容提要

FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量和递归互斥信号量。信号量的API函数都是以宏的方式实现的,使用了现有的队列机制。创建信号量需要包含semphr.h头文件。二进制信号量、计数信号量和互斥量的创建API函数是独立的,但是获取和释放API函数是相同的;递归互斥量的创建、获取和释放API函数是独立的。释放信号量使用xSemaphoreGive()函数,获取信号量使用xSemaphoreTake()函数。递归互斥量可以重复获取和释放。

🔎

延伸解读

信号量本质:基于队列的轻量封装

FreeRTOS的信号量并非独立的内核对象,而是通过宏定义复用队列机制实现。二进制、计数和互斥量创建时都调用xQueueGenericCreate(),但队列项长度设为0,仅利用队列结构体中的uxMessageWaiting成员表示信号量状态。这种设计减少了内核代码冗余,也意味着理解队列操作是掌握信号量行为的基础。

互斥量与二进制信号量的关键差异

互斥量具有优先级继承机制,且必须在同一任务中获取和释放,不可用于中断服务程序;二进制信号量则无优先级继承,可在任意任务或中断中释放和获取。互斥量创建后即处于有效状态,用于保护实际存在的资源;二进制信号量初始无效,常用于任务同步。选择时需根据场景判断是否需要优先级继承和中断安全。

优先级继承如何缓解优先级反转

当高优先级任务因等待低优先级任务持有的互斥量而阻塞时,低优先级任务会临时继承高优先级,从而减少中等优先级任务抢占导致的额外延迟。文章示例中,任务A继承任务C的优先级后,能更快释放互斥量,使任务C等待时间缩短。但优先级继承仅在任务上下文有效,中断中无法使用互斥量。

递归互斥量的使用要点

递归互斥量允许同一任务多次获取,通过uxRecursiveCallCount记录递归次数。每次获取计数加1,释放时减1,只有计数归零时互斥量才真正释放。获取和释放必须成对出现,且需在同一任务中完成。若其他任务尝试释放,将直接返回失败。该功能需将configUSE_RECURSIVE_MUTEXES定义为1才可用。

❓

Q&A

FreeRTOS的信号量有哪几种类型?

FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量和递归互斥信号量。

FreeRTOS中创建二进制信号量、计数信号量和互斥量的API函数有什么区别?

二进制信号量、计数信号量和互斥量的创建API函数是独立的,但获取和释放API函数是相同的;递归互斥量的创建、获取和释放API函数都是独立的。

FreeRTOS中释放和获取信号量分别使用什么函数?

释放信号量使用xSemaphoreGive()函数,获取信号量使用xSemaphoreTake()函数。这两个函数适用于二进制信号量、计数信号量和互斥量。

FreeRTOS的互斥量有什么特殊机制?

互斥量具有优先级继承机制,可以降低优先级反转的影响。互斥量必须在同一任务中获取和释放,且不能用于中断服务程序。

FreeRTOS中递归互斥量如何获取和释放?

递归互斥量使用xSemaphoreTakeRecursive()获取,使用xSemaphoreGiveRecursive()释放。已经获取递归互斥量的任务可以重复获取,但获取几次就要释放几次,直到递归计数器减为0,互斥量才再次有效。

FreeRTOS中信号量的API函数是如何实现的?

信号量的API函数都是以宏的方式实现的,它们使用了现有的队列机制。这些宏定义在semphr.h文件中,使用信号量或互斥量时需要包含该头文件。

🏷️

标签

➡️

继续阅读