互斥量
数值只有0或1
谁获得互斥量,就必须由谁释放同一个互斥量。
但其实在freeRTOS中,任务A获取的互斥锁,任务B也能释放。因此谁上锁谁开锁只是约定,在程序实现上不是强制的。
“可重入的函数"是指:多个任务同时调用它、任务和中断同时调用它,函数的运行也是安全的。可重入的函数也被称为"线程安全”(thread safe)。
每个任务都维持自己的栈、自己的CPU寄存器,如果一个函数只使用局部变量,那么它就是线程安全的。
函数中一旦使用了全局变量、静态变量、其他外设,它就不是"可重入的",如果该函数正在被调用,就必须阻止其他任务、中断再次调用它。
上述问题的解决方法是:任务A访问这些全局变量、函数代码时,独占它,就是上个锁。这些全局变量、函数代码必须被独占地使用,它们被称为临界资源。
互斥量的使用过程如下:
互斥量初始值为1
任务A想访问临界资源,先获得并占有互斥量,然后开始访问
任务B也想访问临界资源,也要先获得互斥量:被别人占有了,于是阻塞
任务A使用完毕,释放互斥量;任务B被唤醒、得到并占有互斥量,然后开始访问临界资源
任务B使用完毕,释放互斥量
互斥量函数
使用互斥量需要定义:
#define configUSE_MUTEXES 1
创建
成功则返回句柄,失败则返回NULL
SemaphoreHandle_t xSemaphoreCreateMutex( void ); // 动态分配
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
获取与释放
不能在中断中使用。除此之外与信号量相同
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );//删除
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );//释放
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);//获取
优先级反转问题
使用信号量时,有时会出现低优先级先运行,高优先级不运行的问题,原因在于:
低优先级的任务先创建,先获取了信号量;
不需要取信号量的中优先级任务第二个创建,阻塞了低优先级任务,此时低优先级任务信号量还没释放;
高优先级任务后创建,此时take信号量,被阻塞,无法运行
在FreeRTOS_信号量_学习笔记中,修改三个任务的优先级:
xTaskCreate(CarTask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);
xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+1, NULL);
xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+2, NULL);
同时将第二辆车的任务中获取信号量和释放信号量的语句删除。此时CarTask被Car2Task阻塞,Car3Task无法运行
此时,只要将信号量替换为互斥量即可解决优先级反转问题。其原理是用了优先级继承功能。
假设持有互斥锁的是任务A,如果更高优先级的任务B也尝试获得这个锁
任务B说:你既然持有宝剑,又不给我,那就继承我的愿望吧
于是任务A就继承了任务B的优先级
这就叫:优先级继承
等任务A释放互斥锁时,它就恢复为原来的优先级
互斥锁内部就实现了优先级的提升、恢复
递归锁
互锁
A获得互斥量M1
B获得互斥量M2
A还想获得M2才能运行,A阻塞
B还想获得M1才能运行,B阻塞
AB都阻塞,都无法释放其所持有的互斥量
自锁
A获得了互斥量M
他调用了一个库函数,库函数也想获取互斥量M
库函数阻塞,A休眠,无法释放互斥量
解决以上问题,可以使用递归锁,其特性为:
- 任务A获得递归锁M后,它还可以多次去获得这个锁
- "take"了N次,要"give"N次,这个锁才会被释放
递归锁的函数和一般互斥量的函数名不一样,参数类型一样。
递归锁 | 互斥量 | |
---|---|---|
创建 | xSemaphoreCreateRecursiveMutex | xSemaphoreCreateMutex |
获得 | xSemaphoreTakeRecursive | xSemaphoreTake |
释放 | xSemaphoreGiveRecursive | xSemaphoreGive |
递归锁主要用于解决同一个线程多次获取同一个锁时的同步问题,不涉及多个线程之间的并发访问。