互斥量即互斥信号量,是特殊的二值信号量,它与信号量不同的是,它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。
任何时刻互斥量的状态只有两种,开锁和闭锁。
当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。释放互斥量时,则处于开锁状态。
当一个任务持有互斥量时,其他任务不能再对该互斥量进行开锁或持有。持有该互斥量的任务也能够再次获得这个锁而不被挂起,即递归访问。
实行同步二值信号量是最好的选择,互斥量更多的是用于保护资源的互锁。
优先继承算法:指暂时提高某个占有某种资源的低优先级任务的优先级,使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。
因此继承优先级的任务避免了系统资源被任何中间优先级的任务抢占。所以优先级继承机制确保高优先级任务进入阻塞状态的时间尽可能短。
“优先级翻转”:高优先级任务无法运行而低优先级任务可以运行的现象。
使用互斥量时间的注意点:
在获得互斥量后,要尽快释放互斥量,同时在任务持有互斥量的时间内,不得更改任务的优先级。
多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问。
互斥量不能在中断服务函数中使用,因为其特有的优先级继承机制只在任务起作用,在中断的上下文环境毫无意义。
用互斥量处理不同任务对临界资源的同步访问时,任务想要获得互斥量才能进行资源访问,如果一旦有任务成功获得了互斥量,则互斥量立即变为闭锁状态,此时其他任务会因为获取不到互斥量而不能访问这个资源,任务会根据用户自定义的等待时间进行等待,直到互斥量被持有的任务释放后,其他任务才能获取互斥量从而得以访问该临界资源,此时互斥量再次上锁,如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的安全性。
互斥量的 API 函数实际上都是宏,它使用现有的队列机制。互斥量控制块结构体与消息队列结构体一致,仅在某些成员变量有稍微区别。
用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数
uxMessagesWaiting 被用于互斥量的时候,1表示互斥量有效,0则无效。
uxLength 最大为 1,因为互斥信号量要么是有效的,要么是无效的。
uxItemSize用于互斥量时,无需存储控制,即为0。
该函数会返回一个互斥量句柄,所以使用前用户必须定义一个互斥量句柄。
该函数是带条件编译的,只有将宏 configUSE_MUTEXES 定义为 1 才会编译这个函数。
所以需在 FreeRTOSConfig.h 中把configUSE_MUTEXES 宏定义打开,表示使用互斥量。
用于创建一个递归互斥量。
递归信号量与互斥量一样,都实现了优先级继承机制,可以降低优先级反转的危害。
必须在FreeRTOSConfig.h 中把宏configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_MUTEXES 均定义为 1。
本质为信号量删除。
为一个宏,实际调用的是消息队列获取函数。但有点区别:这个函数里面会使用宏定义进行编译,如果获取的对象是互斥量,那么这个函数就拥有优先级继承算法,如果获取对象不是互斥量,就没有优先级继承机制。
大概过程:
如果互斥量有效,调用获取互斥量函数后结构体成员变量 uxMessageWaiting 会减 1,指针 pxMutexHolder 指向任务控制块,表示这个互斥量被哪个任务持有, 只有这个任务才拥有互斥量的所有权 ,并且该uxMutexesHeld 会加 1,表示任务已经获取到互斥量。
如果此时互斥量是无效状态并且用户指定的阻塞时间为 0,则直接返回错误码(errQUEUE_EMPTY)。
而如果用户指定的阻塞超时时间不为 0,则当前任务会因为等待互斥量有效而进入阻塞状态,在将任务添加到延时列表之前,会判断当前任务和拥有互斥量的任务优先级哪个更高,如果当前任务优先级高,则拥有互斥量的任务继承当前任务优先级。
使用该函数前提:互斥量之前必须由xSemaphoreCreateRecursiveMutex()这个函数创建。
该函数不能用于获取由函数xSemaphoreCreateMutex()创建的互斥量。
要想使用该函数必须在头文件 FreeRTOSConfig.h 中把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。
互斥量的释放函数与信号量的释放函数一致,都是调用 xSemaphoreGive()函数,但是互斥量的释放只能在任务中,不允许在中断中释放互斥量。
只有已持有互斥量所有权的任务才能使用该函数来释放它,当任务调用xSemaphoreGive()函数时会将互斥量变为开锁状态,等待获取该互斥量的任务将被唤醒。
如果任务的优先级被互斥量的优先级翻转机制临时提升,那么当互斥量被释放后,任务的优先级将恢复为原本设定的优先级。
要想使用该函数必须在头文件 FreeRTOSConfig.h 把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。
使用 xSemaphoreTakeRecursive() 函数成功获取几次递归互斥量,就要使用 xSemaphoreGiveRecursive()函数返还几次,在此之前递归互斥量都处于无效状态,别的任务就无法获取该递归互斥量。
每释放一次该递归互斥量,它的计数值就减 1。当该互斥量的计数值为 0 时(即持有任务已经释放所有的持有操作),互斥量则变为开锁状态,等待在该互 斥量上的任务将被唤醒。