FreeRTOS-互斥量-2

1. 互斥量控制块

因为互斥量是特殊的二值信号量,信号量是特殊的队列,那么互斥量的控制块也是类似消息队列的控制块。源码如下:

(1)pcReadFrom 与 uxRecursiveCallCount是一对互斥变量。使用联合体union确保两个互斥变量的结构体成员不会同时出现。当结构体用于队列pcReadFrom指向最后一个消息空间 (出队消息);当结构体用于互斥量 时,uxRecursiveCallCount 用于计数,记录递归互斥量被"调用"的次数

(1)在互斥量控制块中,uxMessageWaiting代表有效互斥量的个数,1表示互斥量有效;0表示互斥量无效。

(2)(3)互斥量的应用中,最大信号量可用个数(队列长度)uxLength被设置为1;单个消息的大小被设置为0,互斥量无序存储空间。

2. 互斥量函数接口

2.1. 互斥量创建函数xSemaphoreCreateMutex()

xSemaphoreCreateMutex()用于创建一个互斥量,并返回一个互斥量句柄。句柄原型是一个(void *)指针,使用之前需要再main.c中定义一个互斥量句柄。使用该函数需要在FreeRTOSConfig.h文件中把宏configSUPPORT_DYNAMIC_ALLOCATION定义为1,还需要把configUSE_MUTEXES宏定义打开,表示使用互斥量(mutexes)。

下面是xSemaphoreCreateMutex()的原型,其实就是调用了后面的xQueueCreateMutex函数:

(1)使用通用队列创建函数xQueueGenericCreate()函数创建互斥量。uxMutexLength=1;uxMutexSize=0;ucQueueType实际上被设置为queueQUEUE_TYPE_MUTEX(互斥量)。

(2)使用prvInitialiseMutex()初始化互斥量。prvInitialiseMutex()源码如下:

(4)这里有几个宏定义,主要就是为了把队列的控制块变成互斥量的控制块。因为互斥量不用理会消息存储区域,所以原本在消息队列控制块中用于指向消息存储区域的pcTail和pcHead指针变成了pxMutexHolder和uxQueueType。**pxMutexHolder被用于指向持有互斥量的任务的TCB(为优先级继承机制做准备);**uxQueueType表示队列类型,被设置为queueQUEUE_IS_MUTEX(NULL),表示用作互斥量。

(1)初始化的时候互斥量为开锁状态,没有任务持有该互斥量。

(2)如果是递归互斥量,还需要初始化联合体成员u.uxRecursiveCallCount为0,被调用0次。

(3)调用xQueueGenericSend()释放互斥量,在互斥量创建成功的时候默认互斥量是开锁状态。

成功创建后,互斥量示意图如下:

使用实例,在使用之前需要用户自己定义一个互斥量控制块指针(句柄?)

2.2. 递归互斥量创建函数xSemaphoreCreateRecursiveMutex()

xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量。递归互斥量可以被同一个任务获取多次,获取多少次就需要释放多少次。递归互斥量也有优先级继承机制。

想要使用该函数,需要把FreeRTOSConfig.h中把configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_MUTEXES 都设置为1.

xSemaphoreCreateRecursiveMutex()的说明如下:

本质上,xSemaphoreCreateRecursiveMutex()就是调用xQueueCreateMutex()函数(多了一个u.uxRecursiveCallCount记录互斥量被递归调用的次数)。

下面是xSemaphoreCreateRecursiveMutex()的使用实例:

2.3. 互斥量删除函数vSemaphoreDelete()

其实就是信号量删除函数,我们直接调用vSemaphoreDelete()函数,传入互斥量句柄作为形参即可。

2.4. 互斥量获取函数xSemaphoreTake()

任务队对互斥量的所有权是独占的,任意时刻互斥量只能被一个任务获得,并拥有互斥量的使用权;如果互斥量处于闭锁状态,获取该互斥量的任务将无法获得互斥量,任务被挂起,在任务被挂起之前,如果当前获得互斥量的任务优先级比挂起的任务优先级低,那么将会临时提升持有互斥量任务的优先级。

互斥量获取函数也是宏定义,实际调用的函数就是xQueueGnericReceive():

xQueueGenericReceive()函数就是消息队列获取消息函数,只不过使用互斥量的时候这个函数就多了一个优先级继承算法 ,源码如下:

(1)互斥量有效,获取成功后结构体成员变量pxMutexHolder指向获得互斥量的任务的TCB。调用pvTaskIncrementMutexHeldCount()函数把当前任务控制块的成员uxMutexesHeld加1,并返回当前TCB的指针pxCurrentTCB给指针pxMutexHolder。

(2)互斥量无效,当前任务无法获取互斥量,并且用户指定了阻塞时间。那么,调用vTaskPriorityInherit()函数进行优先级继承,然后任务进入阻塞态。

其实和操作消息队列获取消息没什么区别,主要过程如下:

如果互斥量有效,调用获取互斥量函数后结构体成员变量uxMessageWaiting减1,队列结构体成员指针pxMutexHolder指向获取互斥量的任务的TCB,该任务的TCB成员uxMetxesHeld会加1,表示任务得到了互斥量。

如果互斥量无效,则任务进入阻塞态,等待用户指定的阻塞时间(阻塞时间为0直接返回错误码),并且在将任务添加到延时列表之前,会判断当前持有互斥量的任务和进入阻塞的任务哪个的优先级高,如果进入阻塞的任务优先级高,则短暂提升持有互斥量的任务的优先级到进入阻塞的任务的优先级级别(优先级继承机制)。

优先级继承机制函数vTaskPriorityInherit()函数源码如下

(1)获取持有互斥量的任务的TCB。

(2)判断优先级高低。如果持有互斥量的任务比等待任务优先级低,则进行优先级继承。

(3)如果持有互斥量的任务在等待事件列表 中,就调整持有互斥量的任务所等待的事件列表的优先级,因为待会会暂时修改持有互斥量任务的优先级。

(4)如果被提升优先级的任务处于就绪列表中,则就绪列表中的任务都要重新排序

(5)现将提升优先级的任务从就绪列表中删除,待优先级继承完之后重新插入就绪列表。

(6)修改持有互斥量任务的优先级,

(7)重新 将修改优先级后的持有互斥量的任务插入就绪列表。

(8)如果持有互斥量的任务不在就绪列表,那直接提升优先级。
TIPS:如果任务获取互斥量成功,那么在使用完毕需要
立即释放,否则很容易造成其他任务无法获取互斥量,因为互斥量的优先级继承机制是只
能将优先级危害降低,而不能完全消除。同时还需注意的是,互斥量是不允许在中断中操
作的,因为优先级继承机制在中断是无意义的,

使用实例:

2.5. 递归互斥量获取函数xSemaphoreTakeRe()

xSemaphoreTakeRecursive()是一个用于递归获取互斥量的宏。要想使用该函数必须在头文件 FreeRTOSConfig.h中把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。

(1)判断持有递归互斥量的任务是不是当前要获取的任务。如果是则只需要u.uxRecursiveCallCount自加,表明任务调用了多少次递归互斥量。

(2)如果不是同一个任务去获取递归互斥量,则按照互斥量的性质,当互斥量有效才能获取互斥量,否则阻塞等待。

(3)如果获取成功,则u.uxRecursiveCallCount自加。否则调用函数返回错误

pxMutexHolder指向获取递归互斥量的任务块。xSmeaphoreTakeRecursive()函数使用实例如下:

2.6. 互斥量释放函数xSemaphoreGive()

任务使用完资源后,需要及时归还对应的互斥量。xSemaphoreGive()就是用来释放互斥量的。需要注意的是,互斥量只能在任务中释放,不能在中断中释放。只有持有互斥量的任务才能释放它,调用xSemaphoreGive()函数后,互斥量会变为开锁状态,等待获取该互斥量的任务将被唤醒,如果该任务的任务优先级被互斥量的优先级翻转机制临时提升,那么但互斥量被释放的时候,任务优先级将恢复为员额不能设定的优先级,如下:

其实就是调用了xQueueGenericSend()函数,不一样的地方就是他的优先级继承机制,释放互斥量的时候需要还原任务优先级。prvCopyDataToQueue()这个函数就是用来恢复优先级的,在xQueueGenericSend()中被调用。

这里又调用了xTaskPriorityDisinherit()函数来恢复优先级,并且将结构体pxMutexHolder指向NULL,也就是说暂时没有任务持有互斥量。下面看看xTaskPriorityDisinherit():

(1)判断是否有任务持有互斥量,必须是持有互斥量的任务才能释放互斥量。

(2)判断优先级是否被提升,没被提升直接退出

(3)康康任务是否还有其他互斥量,有多个互斥量的任务不能随便恢复优先级

(4)将任务从状态列表删除,因为要恢复优先级

(5)恢复优先级

(6)重置等待事件列表优先级。

(7)将任务重新就绪

释放互斥量的过程:

1、互斥量释放后才有效,并且需要结构体成员uxMessageWaiting加一;

2、判断持有互斥量的任务是否有优先级继承,有的话恢复优先级;

3、判断是否有任务因为无法获取该互斥量进入阻塞,有的话唤醒。

使用实例:

2.7. 递归互斥量释放函数xSemaphoreGiveRecursive()

xSemaphoreGiveRecursive()是一个用于释放递归互斥量的宏。要想使用该函数必须在头文件 FreeRTOSConfig.h 把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。

TIPS:递归互斥量可以被重复获取,而获取多少次递归互斥量就需要释放多少次该互斥量。也就是说调用多少次xSemaphoreTakeRecursive() ,就需要调用多少次xSemaphoreGiveRecursive()。
使用该函数接口时,只有已持有互斥量所有权的任务才能释放它,每释放一次该递归互斥量,它的计数值就减 1。当该互斥量的计数值为 0 时(即持有任务已经释放所有的持有操作),互斥量则变为开锁状态,等待在该互斥量上的任务将被唤醒。如果任务的优先级被互斥量的优先级翻转机制临时提升,那么当互斥量被释放后,任务的优先级将恢复为原本设定的优先级,

使用实例:

相关推荐
啊我不会诶2 小时前
2025 北京市大学生程序设计竞赛暨“小米杯”全国邀请赛
c++·学习·算法
mit6.8242 小时前
懒更新|单点查询
算法
Yupureki2 小时前
《C++实战项目-高并发内存池》8. 最终性能优化与测试
c语言·开发语言·数据结构·c++·算法·性能优化
DeepModel2 小时前
【概率分布】均匀分布的原理、推导与Python实现
python·算法·概率论
一叶落4382 小时前
LeetCode 74 | 搜索二维矩阵(C语言版题解)
c语言·数据结构·c++·算法·leetcode·矩阵·动态规划
罗湖老棍子2 小时前
星际信号塔 —— 单调栈经典应用详解
数据结构·算法·单调栈
iAkuya2 小时前
(leetcode)力扣100 96.只出现一次的数字(位运算)
算法·leetcode·职场和发展
Tisfy2 小时前
LeetCode 1622.奇妙序列:懒更新
数学·算法·leetcode·题解·设计
無限進步D2 小时前
高精度算法 cpp
c++·笔记·算法·入门