FreeRTOS 信号量(三) ------ 优先级翻转

news2024/11/16 6:30:59

一、优先级翻转

在这里插入图片描述

(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,当任务 M 等待的事件发生 后,任务 M 剥夺了任务L 的 CPU 使用权。

(9) 任务 M 处理该处理的事。

(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。

(11) 任务 L 继续运行。

(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。

(13) 任务 H 得到该信号量并接着运行。

在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级翻转。

二、编程实战

1、实验目的
在使用二值信号量的时候会存在优先级翻转的问题,本实验通过模拟的方式实现优先级翻转,观察优先级翻转对抢占式内核的影响。

2、实验设计
本实验设计四个任务:start_task、high_task 、middle_task ,low_task,这四个任务的任务

功能如下:
start_task:用来创建其他 3 个任务。
high_task :高优先级任务,会获取二值信号量,获取成功以后会进行相应的处理,处理完成以后就会释放二值信号量。
middle_task :中等优先级的任务,一个简单的应用任务。
low_task:低优先级任务,和高优先级任务一样,会获取二值信号量,获取成功以后会进行相应的处理,不过不同之处在于低优先级的任务占用二值信号量的时间要久一点(软件模拟占用)。

实验中创建了一个二值信号量 BinarySemaphore,高优先级和低优先级这两个任务会使用这个二值信号量。

3、实验程序与分析
●任务设置

#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 };

● 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(); //开启任务调度
}

● 任务函数

//开始任务任务函数
void start_task(void *pvParameters)
{
	taskENTER_CRITICAL(); //进入临界区
	//创建二值信号量
	BinarySemaphore=xSemaphoreCreateBinary(); (1)
	//二值信号量创建成功以后要先释放一下
	if(BinarySemaphore!=NULL)xSemaphoreGive(BinarySemaphore); (2)
	//创建高优先级任务
	 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); //获取二值信号量 (3)
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充区域
		LED1=!LED1;
		xSemaphoreGive(BinarySemaphore); //释放信号量 (4)
		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); //获取二值信号量 (5)
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) //模拟低优先级任务占用二值信号量 (6)
		{
			taskYIELD(); //发起任务调度
		}
	xSemaphoreGive(BinarySemaphore); //释放二值信号量 (7)
	vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
	}
}

(1)、调用函数 xSemaphoreCreateBinary()创建二值信号量。

(2)、默认创建的二值信号量是无效的,这里需要先调用函数 xSemaphoreGive()释放一次二值信号量。否则任务 high_task()和 low_task()都会获取不到信号量。

(3)、高优先级任务调用函数 xSemaphoreTake()获取二值信号量。

(4)、使用完以后需要调用函数 xSemaphoreGive()释放二值信号量。

(5)、低优先级任务获取二值信号量 BinarySemaphore。

(6)、低优先级任务模拟长时间占用二值信号量。

(7)、低优先级任务释放二值信号量。

程序运行结果分析
(1)、low_task 任务获取到二值信号量 BinarySemaphore 开始运行。

(2)、high_task 获取信号量 BinarySemaphore,但是此时信号量 BinarySemaphore 被任务low_task 占用着,因此 high_task 就要一直等待,直到 low_task 任务释放信号量 BinarySemaphore。

(3)、由于 high_task 没有获取到信号量 BinarySemaphore,只能一直等待,红色部分代码中high_task 没有运行,而 middle_task 一直在运行,给人的感觉就是 middle_task 的任务优先级高于 high_task。但是事实上 high_task 任务的任务优先级是高于 middle_task 的,这个就是优先级反转!

(4)、high_task 任务因为获取到了信号量 BinarySemaphore 而运行.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/466848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql慢查询日志

概念 MySQL的慢查询日志是MySQL提供的一种日志记录&#xff0c;它用来记录在MySQL中响应时间超过阀值的语句&#xff0c;具体指运行时间超过long_query_time值的SQL&#xff0c;则会被记录到慢查询日志中。long_query_time的默认值为10&#xff0c;意思是运行10秒以上的语句。…

计算机图形学 | 投影变化

计算机图形学 | 投影变化 计算机图形学 | 投影变化7.1 有趣的投影投影的概念平行投影正投影斜投影 透视投影 7.2 规范化的投影变换观察的要素观察空间规范化的投影变换 华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff09; 计算…

Flink时间和窗口

事件时间 到达时间 处理时间 水位线 1.有序流 2. 无序流 水位线离源越近越好 Flink 自带水位线 有序 WatermarkStrategy.<Event>forMonotonousTimestamps() 或者实现WatermarkStrategy接口 水位线生成 时间字段 乱序 WatermarkStrategy.<Event>forBoundedOut…

【MySQL高级】——InnoDB索引MyISAM索引

一、索引概述 MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构。 索引的本质&#xff1a;索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”&#xff0c;满足特定查找算法。 这些数据结构以某种方式指向…

Redis基础知识概述

Redis基础知识概述 文章目录 Redis基础知识概述一、Redis简介二、NoSQL技术三、Redis的高并发和快速原因四、Redis为什么是单线程的 五、单线程的优劣势1、优势2、劣势 六、Redis高并发总结七、在java中使用Redis1、添加Jedis依赖 八、Redis在Java Web中的应用1、存储缓存用的数…

亿发软件:按需定制ERP管理解决方案,更合适的企业智能管理软件

亿发软件&#xff1a;按需定制ERP管理解决方案&#xff0c;更合适的企业智能管理软件 在当今瞬息万变的商业环境中&#xff0c;企业面临着新的挑战和机遇。随着国内传统市场的衰落和国际化发展的加速&#xff0c;市场竞争日趋激烈&#xff0c;企业必须寻找新的创新和适应方式才…

Linux基本指令和操作(2)

目录 一. 适配符 * 二. man指令 -- 查看手册 三. echo指令 -- 输出字符串到文件 四. cp指令 -- 复制 五. mv指令 -- 重命名或移动文件&#xff08;剪切&#xff09; 六. which指令 -- 查看指令所在的路径 七. alis指令 -- 指令重命名 八. cat指令 -- 输出文件内容 九…

7.微服务项目实战---Rocketmq--消息驱动

7.1 MQ简介 7.1.1 什么是MQ MQ &#xff08; Message Queue &#xff09;是一种跨进程的通信机制&#xff0c;用于传递消息。通俗点说&#xff0c;就是一个先进先出的数据结构。 7.1.2 MQ的应用场景 7.1.2.1 异步解耦 最常见的一个场景是用户注册后&#xff0c;需要发送注…

怎么注册Google账号(使用国内手机号注册)

怎么注册Google账号&#xff08;使用国内手机号注册&#xff09; 记录一下如何用 国内的手机号 注册Google账号 文章目录 怎么注册Google账号&#xff08;使用国内手机号注册&#xff09;进入Google官网创建账号注册信息填写手机号&#xff08;踩坑版&#xff09;填写手机号&am…

MySQL——超详细数据库触发器教程

文章目录 一、触发器的概念 二、创建触发器 三、查看触发器 四、删除触发器 一、触发器的概念 在实际开发中往往会碰到这样的情况&#xff1a; 当我们对一个表进行数据操作时&#xff0c;需要同步对其它的表执行相应的操作&#xff0c;正常情况下&#xff0c;如果我们使用s…

C语言字符串函数,字符函数,内存操作函数

提示&#xff1a; 本篇文章涉及到以下内容: 求字符串长度 strlen 长度不受限制的字符串函数(被VS认为不安全,就像scanf)–>非法也要完成任务 strcpy 拷贝(将原字符串内容和\0全拷贝过去) strcat 追加(先找到目标空间中的\0,然后把原字符串中的内容直到\0全拷贝过去,原字符串…

【RPA开发】lxml 库之 etree 使用详解

通过 requests.get 方法获得 html 源代码后&#xff0c;可以通过 etree 进行解析&#xff0c;进而从源代码中提取关键信息。etree 同 Beautiful Soup 一样均可以解析 xml 和 html&#xff0c;两者不同之处在于&#xff1a;etree主要通过 xpath 进行定位&#xff0c;而 Beautifu…

基于Spring Boot+Vue 的校园健康系统设计与实现(附源码,文档)

一 简介 校园健康系统本质上是一个健康知识浏览和在线咨询的平台&#xff0c;从用户角度&#xff0c;系统包括大学生、医生和管理员。 二.主要技术 技术名作用Springboot后端框架Vue前端框架MySQL数据库 三 功能介绍 校园健康系统为用户提供医生预约服务&#xff0c;系统…

Clickhouse分布式表引擎(Distributed)查询核心原理解析

Clickhouse分布式表引擎&#xff08;Distributed&#xff09;查询核心原理解析 Clickhouse分布式表引擎&#xff08;Distributed&#xff09;写入核心原理解析Clickhouse分布式表引擎&#xff08;Distributed&#xff09;查询核心原理解析 与分布式数据写入时可以选择写分布式…

有哪家台灯好又便宜的适合学生党使用?真正合格的小学生台灯

都说眼睛是心灵的窗户&#xff0c;但是现在很多小朋友还没上初中&#xff0c;可能就早早的近视了。究其原因&#xff0c;除了和频繁观看电子屏幕密不可分之外&#xff0c;不良的用眼习惯也是一大关键。孩子写作业时不时揉眼睛的动作&#xff0c;其实只要时间一长&#xff0c;眼…

MYSQL prefer_order_index 的罪责

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Linux运维之初识shell

一.补充知识点 1.系统定时任务 系统定时任务需要用到crontab命令&#xff0c;但是使用此命令有一个前提&#xff0c;即需要打开crond服务。为了不那么复杂&#xff0c;可以直接使用我之前学的systemctl命令重新启动crond服务。 语法&#xff1a;crontab [-e -l -r] 选项&am…

安装zsh-theme oh-my-zsh

安装zsh yum install zsh切换到zsh chsh -s /bin/zsh exec /bin/zsh重启并且查看 echo $SHELL//查看当前shell,如果显示/bin/zsh&#xff0c;则配置成功 安装oh my zsh sh -c "$(wget https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)"到…

浅谈Java线程

大家好&#xff0c;我是易安&#xff01;今天我们简单聊下Java线程这个话题。 在Java领域&#xff0c;实现并发程序的主要手段就是多线程。线程是操作系统里的一个概念&#xff0c;虽然各种不同的开发语言如Java、C#等都对其进行了封装&#xff0c;但是万变不离操作系统。Java语…

您的天气类APP会泄露隐私吗?

不知您是否有这样的习惯&#xff0c;在早上出门前、或是在规划次日的行程时&#xff0c;都会不自觉地掏出手机、点开天气类APP进行查看。此类APP有的是智能手机自带的&#xff0c;有的是从应用商店里下载并获取的第三方应用。无论是哪种&#xff0c;它们往往都有着一个共性&…