个人名片:
🎓作者简介:嵌入式领域优质创作者
🌐个人主页:妄北y📞个人QQ:2061314755
💌个人邮箱:[mailto:2061314755@qq.com]
📱个人微信:Vir2025WBY🖥️个人公众号:科技妄北
🖋️本文为妄北y原创佳作,独家首发于CSDN🎊🎊🎊
💡座右铭:改造世界固然伟大,但改造自我更为可贵。
专栏导航:
妄北y系列专栏导航:
物联网嵌入式开发项目:大学期间的毕业设计,课程设计,大创项目,各种竞赛项目,全面覆盖了需求分析、方案设计、实施与调试、成果展示以及总结反思等关键环节。📚💼💡
QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作。🛠️🔧💭
Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🎉🖥️
深耕Linux应用开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。常见开源库的二次开发,如libcurl、OpenSSL、json-c、freetype等💐📝💡
Linux驱动开发:Linux驱动开发是Linux系统不可或缺的组成部分,它专注于编写特殊的程序——驱动程序。这些程序承载着硬件设备的详细信息,并扮演着操作系统与硬件间沟通的桥梁角色。驱动开发的核心使命在于确保硬件设备在Linux系统上顺畅运作,同时实现与操作系统的无缝集成,为用户带来流畅稳定的体验。🚀🔧💻
Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🌱🚀
非常期待与您一同在这个广阔的互联网天地里,携手探索知识的海洋,互相学习,共同进步。🌐💫🌱 熠熠星光,照亮我们的成长之路
✨✨ 欢迎订阅本专栏,对专栏内容任何问题都可以随时联系博主,共同书写属于我们的精彩篇章!✨✨
文章介绍:
📚本篇文章将深入剖析RTOS学习的精髓与奥秘,与您一同分享相关知识!🎉🎉🎉
若您觉得文章尚可入目,期待您能慷慨地送上点赞、收藏与分享的三连支持!您的每一份鼓励,都是我创作路上源源不断的动力。让我们携手并进,共同奔跑,期待在顶峰相见的那一天,共庆辉煌!🚀🚀🚀
🙏衷心感谢大家的点赞👍、收藏⭐和评论✍️,您的支持是我前进的动力!
目录:
目录:
一、 什么是优先级翻转?
二、程序设计:
2.1 设计目的
2.2 任务设置:
2.3 main()函数:
2.4 任务函数:
三、程序运行结果分析:
3.1 任务和信号量的状态:
3.2 优先级反转的现象:
3.3 优先级反转的问题:
3.4 解决优先级反转的方法:
一、 什么是优先级翻转?
在使用二值信号量的过程中,优先级翻转是一个常见且重要的问题。在可剥夺的内核环境中,优先级翻转现象尤其普遍,这在实时系统中是不可接受的,因为它会扰乱任务的预定执行顺序,并可能导致严重的后果。图1-1 展示了优先级翻转的一个示例。
在这个场景中,我们可以把任务H、任务M和任务L的执行过程描述为一个优先级翻转的示例。具体情况如下:
1. 任务H和任务M处于挂起状态,等待某个事件发生,而任务L正在正常运行。
2. 当任务L需要访问一个共享资源时,它必须首先获取该资源的信号量。
3. 任务L成功地获得了信号量并开始使用共享资源。
4. 随着时间的推移,任务H由于其优先级高,事件发生后抢占了任务L的CPU使用权。
5. 任务H开始执行。
6. 在任务H运行时,它也需要访问任务L正在使用的共享资源。然而,由于该资源的信号量仍被任务L占用,任务H只能处于挂起状态,等待任务L释放信号量。
7. 任务L继续运行其余的工作。
8. 不久后,任务M由于优先级高于任务L,当它等待的事件发生后,它也剥夺了任务L的CPU使用权。
9. 任务M开始处理其相关事务。
10. 任务M完成任务后,将CPU使用权归还给任务L。
11. 任务L在得到CPU之后继续运行。
12. 最终,任务L完成了所有工作并释放了信号量。由于实时内核意识到有一个高优先级任务(任务H)在等待此信号量,内核进行任务切换。
13. 任务H获得信号量后继续执行。
在此过程中,任务H的优先级实际上被降低至任务L的优先级水平。这是因为任务H必须等待任务L释放其占用的共享资源。而由于任务M抢占了任务L的CPU使用权,任务H的状况变得更加不利,导致优先级翻转现象的发生。这种情况表明,虽然任务H的优先级高于任务L,但由于资源竞争的关系,任务M的优先级实际上高于任务H,从而引发了优先级翻转。
二、程序设计:
2.1 设计目的
本实验设计了四个任务:start task
、high task
、middle task
和low task
,各任务的功能如下:
- start task:负责创建其他三个任务。
- high task:高优先级任务,将获取二值信号量。在成功获取后,执行相关处理,并在完成后释放该信号量。
- middle task:中等优先级任务,作为一个简单的应用任务,功能较为基础。
- low task:低优先级任务,与高优先级任务相似,同样获取二值信号量。在成功获取后,会执行相关处理,但低优先级任务占用信号量的时间较长(通过软件模拟实现)。
在实验过程中,我们创建了一个二值信号量BinarySemaphore
,高优先级任务和低优先级任务都将使用该信号量。
2.2 任务设置:
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LOW_TASK_PRIO 2
//任务堆栈大小
#define LOW_STK_SIZE 256
//任务句柄
TaskHandle_t LowTask_Handler;
//任务函数
void low_task(void *pvParameters);
//任务优先级
#define MIDDLE_TASK_PRIO 3
//任务堆栈大小
#define MIDDLE_STK_SIZE 256
//任务句柄
TaskHandle_t MiddleTask_Handler;
//任务函数
void middle_task(void *pvParameters);
//任务优先级
#define HIGH_TASK_PRIO 4
//任务堆栈大小
#define HIGH_STK_SIZE 256
//任务句柄
TaskHandle_t HighTask_Handler;
//任务函数
void high_task(void *pvParameters);
//二值信号量句柄
SemaphoreHandle_t BinarySemaphore; //二值信号量
//LCD刷屏时使用的颜色
int lcd_discolor[14]={ WHITE, BLACK, BLUE, BRED,
GRED, GBLUE, RED, MAGENTA,
GREEN, CYAN, YELLOW,BROWN,
BRRED, GRAY };
2.3 main()函数:
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
BEEP_Init(); //初始化蜂鸣器
LCD_Init(); //初始化LCD
my_mem_init(SRAMIN); //初始化内部内存池
POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"ATK STM32F103/407");
LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 14-3");
LCD_ShowString(30,50,200,16,16,"Priority Overturn");
LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,90,200,16,16,"2016/11/25");
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
2.4 任务函数:
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建二值信号量
BinarySemaphore=xSemaphoreCreateBinary();
//二值信号量创建成功以后要先释放一下
if(BinarySemaphore!=NULL)xSemaphoreGive(BinarySemaphore);
//创建高优先级任务
xTaskCreate((TaskFunction_t )high_task,
(const char* )"high_task",
(uint16_t )HIGH_STK_SIZE,
(void* )NULL,
(UBaseType_t )HIGH_TASK_PRIO,
(TaskHandle_t* )&HighTask_Handler);
//创建中等优先级任务
xTaskCreate((TaskFunction_t )middle_task,
(const char* )"middle_task",
(uint16_t )MIDDLE_STK_SIZE,
(void* )NULL,
(UBaseType_t )MIDDLE_TASK_PRIO,
(TaskHandle_t* )&MiddleTask_Handler);
//创建低优先级任务
xTaskCreate((TaskFunction_t )low_task,
(const char* )"low_task",
(uint16_t )LOW_STK_SIZE,
(void* )NULL,
(UBaseType_t )LOW_TASK_PRIO,
(TaskHandle_t* )&LowTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//高优先级任务的任务函数
void high_task(void *pvParameters)
{
u8 num;
POINT_COLOR = BLACK;
LCD_DrawRectangle(5,110,115,314); //画一个矩形
LCD_DrawLine(5,130,115,130); //画线
POINT_COLOR = BLUE;
LCD_ShowString(6,111,110,16,16,"High Task");
while(1)
{
vTaskDelay(500); //延时500ms,也就是500个时钟节拍
num++;
printf("high task Pend Sem\r\n");
xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取二值信号量
printf("high task Running!\r\n");
LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充区域
LED1=!LED1;
xSemaphoreGive(BinarySemaphore); //释放信号量
vTaskDelay(500); //延时500ms,也就是500个时钟节拍
}
}
//中等优先级任务的任务函数
void middle_task(void *pvParameters)
{
u8 num;
POINT_COLOR = BLACK;
LCD_DrawRectangle(125,110,234,314); //画一个矩形
LCD_DrawLine(125,130,234,130); //画线
POINT_COLOR = BLUE;
LCD_ShowString(126,111,110,16,16,"Middle Task");
while(1)
{
num++;
printf("middle task Running!\r\n");
LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充区域
LED0=!LED0;
vTaskDelay(1000); //延时1s,也就是1000个时钟节拍
}
}
//低优先级任务的任务函数
void low_task(void *pvParameters)
{
static u32 times;
while(1)
{
xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取二值信号量
printf("low task Running!\r\n");
for(times=0;times<5000000;times++) //模拟低优先级任务占用二值信号量
{
taskYIELD(); //发起任务调度
}
xSemaphoreGive(BinarySemaphore); //释放二值信号量
vTaskDelay(1000); //延时1s,也就是1000个时钟节拍
}
}
三、程序运行结果分析:
编译并下载实验代码到开发板中,打开串口调试助手,默认情况下LCD显示如图 3-1 所示。
图 3-1 LCD默认画面
从LCD上不容易看出优先级翻转的现象,我们可以通过串口很方便的观察优先级翻转,串口输出如图 3-2 所示。
图 3-2 串口输出
为了方便分析,我们将串口输出复制出来,如下:
·LCD ID:5510
middle task Running!
low task Running!
high task Pend Sem
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
middle task Running!
high task Running!
middle task Running!
high task Pend Sem
3.1 任务和信号量的状态:
- 低优先级任务(low_task)获取到二值信号量(BinarySemaphore),并开始运行。
- 高优先级任务(high_task)尝试获取同一个信号量,但由于该信号量被低优先级任务占用,因此高优先级任务必须等待,直到低优先级任务释放信号量。
3.2 优先级反转的现象:
- 在此情况下,尽管高优先级任务(high_task)具有更高的优先级,但由于低优先级任务(low_task)持有信号量,它无法运行。
- 中等优先级任务(middle_task)能够在低优先级任务运行时抢占CPU资源,因此中等优先级任务的执行会导致高优先级任务的延迟,造成高优先级任务在等待信号量的同时被中等优先级任务打断,这就是优先级反转的现象。
3.3 优先级反转的问题:
- 此现象会导致高优先级任务的响应时间显著增加,从而影响系统的实时性能。
3.4 解决优先级反转的方法:
- 一种有效的解决方案是使用互斥信号量(Mutex Semaphore)。互斥信号量可以确保在低优先级任务持有资源时,如果高优先级任务请求资源,系统会提升低优先级任务的优先级到高优先级任务的级别,直到它释放资源。这种方法可以防止中等优先级任务抢占低优先级任务,从而避免优先级反转的发生。
📝大佬觉得本文有所裨益,不妨轻点一下👍给予鼓励吧!
❤️❤️❤️本人虽努力,但能力尚浅,若有不足之处,恳请各位大佬不吝赐教,您的批评指正将是我进步的动力!😊😊😊
💖💖💖若您认为此篇文章对您有所帮助,烦请点赞👍并收藏🌟,您的支持是我前行的最大动力!
🚀🚀🚀任务在默默中完成,价值在悄然间提升。让我们携手共进,一起加油,迎接更美好的未来!🌈🌈🌈