1.互斥量
我们在上一节中讲述了信号量,在使用信号量的时候会出现优先级“翻转”,怎么解决这问题呢,我们就引入互斥量来解决。
问题再现:
任务3需要得到任务1释放信号量之后才可以执行,导致任务2执行而最高级的任务3无法执行。
我们知道如果信号量只有0和1它也算是一个“互斥量”,但是这个互斥量有一些缺陷,比如“优先级翻转”问题。
互斥量的使用场景:
1、解决任务“优先级翻转”,怎么解呢,在互斥量函数内部,会有优先级继承,如果高优先级任务和低优先级任务共同使用同一个互斥量时,高优先级任务会将低优先级任务的优先等级暂时设定跟它一样,这样就保证了低优先级任务不会被阻塞,当低优先级任务释放互斥量后,高优先级任务会将低优先级任务优先级恢复,这样高优先级任务就不会一直阻塞。这是官方对这个创建互斥量函数的部分说明:
2、解决临界资源的使用问题
创建一个互斥量,在使用临界资源前进行Tack,在使用完成后进行Give。这个二进制信号量也是可以的。但是是否能够使用的权利要掌握在自己的手中,最好使用互斥量。
3、其他问题以后补充:······
2.互斥量的内部机制
与二进制信号量不同的是
1.它有优先级继承。
2.并且谁拿这个互斥量谁就得去释放,一对一的关系。
3.互斥量相关函数
只有创建不同,Tack(拿)和Give(释放,给)的函数是相同的。
3.1 创建
互斥量是一种特殊的二进制信号量。
使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。
创建互斥量的函数有2种:动态分配内存,静态分配内存,函数原型如下:
/* 创建一个互斥量,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );
/* 创建一个互斥量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
#define configUSE_MUTEXES 1
3.2 其他函数
要注意的是,互斥量不能在ISR中使用。
各类操作函数,比如删除、give/take,跟一般是信号量是一样的。
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
/* 获得 */
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
4.递归锁
#4.1 死锁的概念
日常生活的死锁:我们只招有工作经验的人!我没有工作经验怎么办?那你就去找工作啊!
假设有2个互斥量M1、M2,2个任务A、B:
- A获得了互斥量M1
- B获得了互斥量M2
- A还要获得互斥量M2才能运行,结果A阻塞
- B还要获得互斥量M1才能运行,结果B阻塞
- A、B都阻塞,再无法释放它们持有的互斥量
- 死锁发生!
#4.2 自我死锁
假设这样的场景:
- 任务A获得了互斥锁M
- 它调用一个库函数
- 库函数要去获取同一个互斥锁M,于是它阻塞:任务A休眠,等待任务A来释放互斥锁!
- 死锁发生!
#4.3 函数
怎么解决这类问题?可以使用递归锁(Recursive Mutexes),它的特性如下:
- 任务A获得递归锁M后,它还可以多次去获得这个锁
- "take"了N次,要"give"N次,这个锁才会被释放
递归锁的函数根一般互斥量的函数名不一样,参数类型一样,列表如下:
递归锁 | 一般互斥量 | |
---|---|---|
创建 | xSemaphoreCreateRecursiveMutex | xSemaphoreCreateMutex |
获得 | xSemaphoreTakeRecursive | xSemaphoreTake |
释放 | xSemaphoreGiveRecursive | xSemaphoreGive |
函数原型如下:
/* 创建一个递归锁,返回它的句柄。*
* 此函数内部会分配互斥量结构体*
* 返回值: 返回句柄,非NULL表示成功*
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
*/ 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
*/ 获得 */
BaseType_t xSemaphoreTakeRecursive(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
5.开发过程遇到的问题总结
待补充~~~