青岛建站方案,python建设电子商务网站,微信商城在哪里点开,易商官方网站目录
前言
一、互斥量概述
二、互斥量函数
1.创建
2.其他函数
三、优先级反转示例
1.概念
2.代码示例
四、优先级继承
1.概念
2.代码示例
五、递归锁
1.死锁的概念
2.自我死锁
3.函数
4.递归锁代码示例 前言
在之前的信号量中#xff0c;我们想要实现互斥的…目录
前言
一、互斥量概述
二、互斥量函数
1.创建
2.其他函数
三、优先级反转示例
1.概念
2.代码示例
四、优先级继承
1.概念
2.代码示例
五、递归锁
1.死锁的概念
2.自我死锁
3.函数
4.递归锁代码示例 前言
在之前的信号量中我们想要实现互斥的效果即独占的享用临界资源假设厕所是临界资源怎么独占自己开门上锁完事了自己开锁。
但是freeRTOS的信号量并没有实现自己上的锁只能自己开这个功能要想实现互斥必须要有个前提条件 没有坏人别的任务不会give信号量不撬门
在我之前的文章关于Linux系统的互斥锁中举得代码例子也是凭空拿了一把锁这就说明要想通过信号量来实现互斥需要程序员自己约定。 一、互斥量概述
可以看到使用信号量确实也可以实现互斥访问但是不完美。
使用互斥量可以解决这个问题互斥量的名字取得很好 量值为0、1 互斥用来实现互斥访问 它的核心在于谁上锁就只能由谁开锁。
很奇怪的是FreeRTOS的互斥锁并没有在代码上实现这点 即使任务A获得了互斥锁任务B竟然也可以释放互斥锁。 谁上锁、谁释放只是约定。
互斥锁解决的核心问题其实是优先级反转和优先级继承。
互斥量其实就是一种特殊的二进制信号量只不过它能解决优先级反转和实现优先级继承。 二、互斥量函数
1.创建
使用互斥量时先创建、然后去获得、释放它。使用句柄来表示一个互斥量。
创建互斥量的函数有2种动态分配内存静态分配内存函数原型如下
/* 创建一个互斥量返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void );/* 创建一个互斥量返回它的句柄。
* 此函数无需动态分配内存所以需要先有一个StaticSemaphore_t结构体并传入它的指针
* 返回值: 返回句柄非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer
);要想使用互斥量需要在配置文件FreeRTOSConfig.h中定义 #define configUSE_MUTEXES 1 注意二级制信号量初始值是0创建后需要Give一次;互斥量初始值是1创建后不需要Give一次。
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
); 三、优先级反转示例
1.概念
假设任务A、B都想使用串口A优先级比较低 任务A获得了串口的互斥量 任务B也想使用串口它将会阻塞、等待A释放互斥量 高优先级的任务被低优先级的任务延迟这被称为优先级反转(priority inversion)
互斥量可以通过优先级继承可以很大程度解决优先级反转的问题这也是FreeRTOS中互斥量和二进制信号量的差别。
2.代码示例
main函数创建了3个任务LPTask/MPTask/HPTask(低/中/高优先级任务)代码如下
/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;
int main( void )
{prvSetupHardware();/* 创建二进制信号量 */xLock xSemaphoreCreateBinary();if( xLock ! NULL ){/* 创建3个任务: LP,MP,HP(低/中/高优先级任务)*/xTaskCreate( vLPTask, LPTask, 1000, NULL, 1, NULL );xTaskCreate( vMPTask, MPTask, 1000, NULL, 2, NULL );xTaskCreate( vHPTask, HPTask, 1000, NULL, 3, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建互斥量/二进制信号量 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}注意创建的是二进制信号量用于演示优先级反转
LPTask/MPTask/HPTask三个任务的代码和运行过程如下图所示 AHPTask优先级最高它最先运行。在这里故意打印这样才可以观察到flagHPTaskRun的脉 冲。 HP DelayHPTask阻塞 BMPTask开始运行。在这里故意打印这样才可以观察到flagMPTaskRun的脉冲。 MP DelayMPTask阻塞 CLPTask开始运行获得二进制信号量然后故意打印很多字符 DHP Delay时间到HPTask恢复运行它无法获得二进制信号量一直阻塞等待 EMP Delay时间到MPTask恢复运行它比LPTask优先级高一直运行。导致LPTask无法 运行自然无法释放二进制信号量于是HPTask无法运行。
总结 LPTask先持有二进制信号量 但是MPTask抢占LPTaskLPTask一直无法运行也就无法释放信号量 导致HPTask任务无法运行 优先级最高的HPTask竟然一直无法运行
程序运行的时序图如下 四、优先级继承
1.概念
上一个代码的问题在于LPTask低优先级任务获得了锁但是它优先级太低而无法运行。
如果能提升LPTask任务的优先级让它能尽快运行、释放锁优先级反转的问题不就解决了吗
优先级继承 假设持有互斥锁的是任务A如果更高优先级的任务B也尝试获得这个锁 任务B说你既然持有宝剑又不给我那就继承我的愿望吧 于是任务A就继承了任务B的优先级 这就叫优先级继承 等任务A释放互斥锁时它就恢复为原来的优先级 互斥锁内部就实现了优先级的提升、恢复
2.代码示例
基于上一个代码我们只需要做一个简单的修改
int main( void )
{prvSetupHardware();/* 创建互斥量 *///xLock xSemaphoreCreateBinary( );xLock xSemaphoreCreateMutex();
创建一个互斥量即可运行时序图如下图所示 AHPTask执行 xSemaphoreTake(xLock, portMAX_DELAY); 它的优先级被LPTask继承 BLPTask抢占MPTask运行 CLPTask执行 xSemaphoreGive(xLock); 它的优先级恢复为原来值 DHPTask得到互斥锁开始运行 互斥锁的优先级继承可以减小优先级反转的影响 五、递归锁
1.死锁的概念
在我之前的博客讲互斥锁的时候也讲过什么情况下会造成死锁例如线程 A 持有锁 L1 并请求锁 L2而线程 B 持有锁 L2 并请求锁 L1这种情况可能导致死锁。所以我们编写程序时要注意不要写成死锁的情况等以后写一些大型的代码需要同时操控几个互斥量时可能会犯这种错误。
假设有2个互斥量M1、M22个任务A、B A获得了互斥量M1 B获得了互斥量M2 A还要获得互斥量M2才能运行结果A阻塞 B还要获得互斥量M1才能运行结果B阻塞 A、B都阻塞再无法释放它们持有的互斥量 死锁发生
2.自我死锁
假设这样的场景 任务A获得了互斥锁M 它调用一个库函数 库函数要去获取同一个互斥锁M于是它阻塞任务A休眠等待任务A来释放互斥锁 死锁发生一个任务申请锁后调用其他函数时申请同一个锁导致要释放锁的任务阻塞从而无法释放锁。
3.函数
怎么解决这类问题可以使用递归锁(Recursive Mutexes)它的特性如下 任务A获得递归锁M后它还可以多次去获得这个锁 take了N次要giveN次这个锁才会被释放
递归锁的函数和一般互斥量的函数名不一样参数类型一样列表如下 函数原型如下
/* 创建一个递归锁返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );/* 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );/* 获得 */
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait
);注意要使用递归锁需要定义配置项configUSE_RECURSIVE_MUTEXES 4.递归锁代码示例
递归锁实现了谁上锁就由谁解锁。
main函数里创建了2个任务 任务1高优先级一开始就获得递归锁然后故意等待很长时间让任务2运行 任务2低优先级看看能否操作别人持有的锁 main函数代码如下
/* 递归锁句柄 */
SemaphoreHandle_t xMutex;
int main( void )
{prvSetupHardware();/* 创建递归锁 */xMutex xSemaphoreCreateRecursiveMutex( );if( xMutex ! NULL ){/* 创建2个任务: 一个上锁, 另一个自己监守自盗(看看能否开别人的锁自己用)*/xTaskCreate( vTakeTask, Task1, 1000, NULL, 2, NULL );xTaskCreate( vGiveAndTakeTask, Task2, 1000, NULL, 1, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建递归锁 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}两个任务经过精细设计代码和运行流程如下图所示 A任务1优先级最高先运行获得递归锁 B任务1阻塞让任务2得以运行 C任务2运行看看能否获得别人持有的递归锁不能 D任务2故意执行give操作看看能否释放别人持有的递归锁不能 E任务2等待递归锁 F任务1阻塞时间到后继续运行使用循环多次获得、释放递归锁 递归锁在代码上实现了谁持有递归锁必须由谁释放。 程序运行结果如下图所示