FreeRTOS互斥量解决优先级反转问题

FreeRTOS互斥量

目录

  • FreeRTOS互斥量
    • 一、概念
    • 二、优先级反转
    • 三、互斥量解决优先级反转

一、概念

FreeRTOS中的互斥量(Mutex)是一种特殊的二值信号量,它支持互斥量所有权、递归访问以及防止优先级翻转的特性。在FreeRTOS中,互斥量用于实现对临界资源的独占式处理。

互斥量的状态只有两种,开锁或闭锁。当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。当该任务释放这个互斥量时,该互斥量处于开锁状态,任务失去该互斥量的所有权。当一个任务持有互斥量时,其他任务将不能再对该互斥量进行开锁或持有。

此外,持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问。在信号量中,由于已经不存在可用的信号量,任务递归获取信号量时会发生主动挂起任务最终形成死锁。这是互斥量与一般的信号量的一个重要区别。

互斥量的典型应用场景:优先级反转

二、优先级反转

实验目的:实现优先级反转现象

优先级反转是指在某些操作系统中,由于任务调度和优先级分配的原因,高优先级的任务可能无法获得足够的CPU时间,而低优先级的任务反而占用大量CPU时间

具体实现:

创建3个Task(Task1、Task2、Task3)、1个二进制信号量、3个全局变量(cnt1、cnt2、cnt3)初始值为0

Task1:

  • 优先级最低
  • 初始化完毕后直接运行
  • 获取信号量
  • 累加cnt1到1000000后释放信号量(累加到1000000时长大于vTaskDelay(300))

Task2:

  • 优先级适中
  • 初始化完毕后vTaskDelay(150)再运行(目的是确保Task1获取了信号量)
  • 一直运行

Task3:

  • 优先级最高
  • 初始化完毕后vTaskDelay(300)再运行(目的是确保Task1获取了信号量,Task2也在运行了)
  • 获取信号量
  • 累加cnt3到1000000后释放信号量

实现代码:

#define mainDELAY_LOOP_COUNT 1000000

uint32_t cnt1 = 0;
uint32_t cnt2 = 0;
uint32_t cnt3 = 0;

uint8_t taskFlag = 0;
SemaphoreHandle_t SemaphoreHandle;

void vTask1( void *pvParameters )
{
	/* 打印任务1的信息 */
	printf( "Task1 start!\r\n");	

	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{	
		/* 表示Task1在运行 */
		taskFlag = 1;
		cnt1++;

		printf( "Task1 Take!\r\n");	
		/* 获取二进制信号量 */
		xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		for(cnt1 = 0; cnt1 < mainDELAY_LOOP_COUNT; cnt1++)
		{

		}
		printf( "Task1 Give!\r\n");
		/* 释放二进制信号量 */
		xSemaphoreGive(SemaphoreHandle);
			
	}
}

void vTask2( void *pvParameters )
{
	
	vTaskDelay(150);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task2在运行 */
		taskFlag = 2;

		
		printf( "Task2 Tack!\r\n");	

	}
}

void vTask3( void *pvParameters )
{
	
	vTaskDelay(300);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task3在运行 */
		taskFlag = 3;

		printf( "Task3 Tack!\r\n");	
		/* 获取二进制信号量 */
		xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		cnt2++;

		for(cnt3 = 0; cnt3 < mainDELAY_LOOP_COUNT; cnt3++)
		{

		}
		printf( "Task3 Give!\r\n");
		/* 释放二进制信号量 */
		xSemaphoreGive(SemaphoreHandle);
			
	}
}

int main( void )
{
	prvSetupHardware();
	
	xTaskCreate(vTask1, "Task 1", 100, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 100, NULL, 2, NULL);
	xTaskCreate(vTask3, "Task 2", 100, NULL, 3, NULL);
	
	/* 创建二进制信号量 */
	SemaphoreHandle = xSemaphoreCreateBinary();

	/* 启动调度器 */
	vTaskStartScheduler();

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

运行结果:

在这里插入图片描述

从测试结果来看:Task1运行后获取信号量,未来得及释放信号量时更高优先级的Task2开始运行,之后Task1没有机会释放信号量,Task3再运行时由于无法获取信号量一直处于阻塞状态,而优先级低于Task3的Task2一直处于运行状态,实现了优先级反转。

三、互斥量解决优先级反转

实验目的:使用互斥量解决优先级反转问题

原理分析:

1、Task1获取互斥量

2、Task1还未释放互斥量Task2开始运行,并一直运行,Task1没有机会释放互斥量

3、Task3运行,获取信号量时发现获取不到,使获取了互斥量的Task1继承自己的优先级(高于Task2)

4、Task1继续运行直到释放互斥量

5、Task3获取信号量,最高优先级的Task3得到运行

具体实现:

创建3个Task(Task1、Task2、Task3)、1个互斥量、3个全局变量(cnt1、cnt2、cnt3)初始值为0

Task1:

  • 优先级最低
  • 初始化完毕后直接运行
  • 获取互斥量
  • 累加cnt1到1000000后释放互斥量(累加到1000000时长大于vTaskDelay(300))

Task2:

  • 优先级适中
  • 初始化完毕后vTaskDelay(150)再运行(目的是确保Task1获取了互斥量)
  • 一直运行

Task3:

  • 优先级最高
  • 初始化完毕后vTaskDelay(300)再运行(目的是确保Task1获取了互斥量,Task2也在运行了)
  • 获取互斥量
  • 累加cnt3到1000000后释放互斥量

实现代码:

#define mainDELAY_LOOP_COUNT 1000000

uint32_t cnt1 = 0;
uint32_t cnt2 = 0;
uint32_t cnt3 = 0;

uint8_t taskFlag = 0;
SemaphoreHandle_t SemaphoreValue;
//SemaphoreHandle_t SemaphoreHandle;

void vTask1( void *pvParameters )
{
	/* 打印任务1的信息 */
	printf( "Task1 start!\r\n");	

	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{	
		/* 表示Task1在运行 */
		taskFlag = 1;
		cnt1++;

		printf( "Task1 Take!\r\n");	
		/* 获取互斥量 */
		xSemaphoreTake(SemaphoreValue, portMAX_DELAY);
		/* 获取二进制信号量 */
		//xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		for(cnt1 = 0; cnt1 < mainDELAY_LOOP_COUNT; cnt1++)
		{

		}
		printf( "Task1 Give!\r\n");
		/* 释放互斥量 */
		xSemaphoreGive(SemaphoreValue);
		/* 释放二进制信号量 */
		//xSemaphoreGive(SemaphoreHandle);
			
	}
}

void vTask2( void *pvParameters )
{
	
	vTaskDelay(150);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task2在运行 */
		taskFlag = 2;

		
		printf( "Task2 Tack!\r\n");	

	}
}

void vTask3( void *pvParameters )
{
	
	vTaskDelay(300);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task3在运行 */
		taskFlag = 3;

		printf( "Task3 Tack!\r\n");	
		/* 获取互斥量 */
		xSemaphoreTake(SemaphoreValue, portMAX_DELAY);
		/* 获取二进制信号量 */
		//xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		cnt2++;

		for(cnt3 = 0; cnt3 < mainDELAY_LOOP_COUNT; cnt3++)
		{

		}
		printf( "Task3 Give!\r\n");
		/* 释放互斥量 */
		xSemaphoreGive(SemaphoreValue);
		/* 释放二进制信号量 */
		//xSemaphoreGive(SemaphoreHandle);
			
	}
}

int main( void )
{
	prvSetupHardware();
	
	xTaskCreate(vTask1, "Task 1", 100, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 100, NULL, 2, NULL);
	xTaskCreate(vTask3, "Task 2", 100, NULL, 3, NULL);
	
	/* 创建互斥量 */
	SemaphoreValue = xSemaphoreCreateMutex();
	/* 创建二进制信号量 */
	//SemaphoreHandle = xSemaphoreCreateBinary();

	/* 启动调度器 */
	vTaskStartScheduler();

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

从运行结果看,将信号量换成互斥量能够解决优先级反转问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch中池化函数详解

1 池化概述 1.1 什么是池化 池化层是卷积神经网络中常用的一个组件&#xff0c;池化层经常用在卷积层后边&#xff0c;通过池化来降低卷积层输出的特征向量&#xff0c;避免出现过拟合的情况。池化的基本思想就是对不同位置的特征进行聚合统计。池化层主要是模仿人的视觉系统…

人工智能:网络犯罪分子的驱动力

随着 2024 年的临近&#xff0c;是时候展望今年的网络安全状况了。由于网络犯罪日益复杂&#xff0c;预计到 2025 年&#xff0c;全球网络安全成本将增至 10.5 万亿美元。 人工智能的使用不断发展&#xff0c;网络犯罪分子变得越来越有创造力 我们注意到&#xff0c;联邦调查…

2023年12月25日:串口发出控制命令

代码 uart4.c #include "uart4.h"void uart4_config() {//*****************************************//使能GPIOB|GPIOG|UART4外设时钟RCC->MP_AHB4ENSETR |(0x1<<6);RCC->MP_AHB4ENSETR |(0x1<<1);RCC->MP_APB1ENSETR |(0x1<<16);RCC…

云计算:现代技术的基本要素

众所周知&#xff0c;在儿童教育的早期阶段&#xff0c;幼儿园都会传授塑造未来行为的一些基本准则。 今天&#xff0c;我们可以以类似的方式思考云计算&#xff1a;它已成为现代技术架构中的基本元素。云现在在数字交互、安全和基础设施开发中发挥着关键作用。云不仅仅是另一…

小狐狸GPT付费2.4.9 去除授权弹窗版

后台安装步骤&#xff1a; 1、在宝塔新建个站点&#xff0c;php版本使用7.2 、 7.3 或 7.4&#xff0c;把压缩包上传到站点根目录&#xff0c;运行目录设置为/public 2、导入数据库文件&#xff0c;数据库文件是 /db.sql 3、修改数据库连接配置&#xff0c;配置文件是/.env 4、…

LED驱动电源

LED驱动电源 常用电子元器件 TB62726AFG LED SOP-24 文章目录 LED驱动电源前言一、LED驱动电源是什么二、TB62726AFG LED SOP-24总结 前言 LED驱动电源可以根据应用需求采用不同的输入和输出电源类型、电源转换拓扑、调光方式等。常见的LED驱动电源类型包括恒流驱动电源、恒…

3分钟了解安全数据交换系统有什么用!

企业为了保护核心数据安全&#xff0c;都会采取一些措施&#xff0c;比如做网络隔离划分&#xff0c;分成了不同的安全级别网络&#xff0c;或者安全域&#xff0c;接下来就是需要建设跨网络、跨安全域的安全数据交换系统&#xff0c;将安全保障与数据交换功能有机整合在一起&a…

uni-app封装表格组件

组件代码&#xff1a; <template><view><uni-table class"tableBox" border stripe emptyText"暂无更多数据" ><!-- 表头行 --><uni-tr class"tableTr"><uni-th align"center" v-for"item in …

LNPMariadb数据库分离|web服务器集群

LNP&Mariadb数据库分离&#xff5c;web服务器集群 网站架构演变单机版LNMP独立数据库服务器web服务器集群与Session保持 LNP与数据库分离1. 准备一台独立的服务器&#xff0c;安装数据库软件包2. 将之前的LNMP网站中的数据库迁移到新的数据库服务器3. 修改wordpress网站配置…

共享和独享的区别是什么?有必要用独享IP吗?

通俗地讲&#xff0c;共享IP就像乘坐公共汽车一样&#xff0c;您可以到达目的地&#xff0c;但将与其他乘客共享旅程&#xff0c;座位很可能是没有的。独享IP就像坐出租车一样&#xff0c;您可以更快到达目的地&#xff0c;由于车上只有您一个人&#xff0c;座位是您一个人专用…

【技术分享】单网口远程透传网关快速实现昆仑通态触摸屏程序远程上下载及监控

准备工作 一台可联网操作的电脑一台单网口的远程透传网关及博达远程透传配置工具网线一条&#xff0c;用于实现网络连接和连接触摸屏一台昆仑通态触摸屏及其编程软件一张4G卡或WIFI天线实现通讯(使用4G联网则插入4G SIM卡&#xff0c;WIFI联网则将WIFI天线插入USB口&#xff0…

String 的特点是什么?它有哪些重要的方法?

几乎所有的 Java 面试都是以 String 开始的&#xff0c;如果第一个问题没有回答好&#xff0c;则会给面试官留下非常不好的第一印象&#xff0c;而糟糕的第一印象则会直接影响到自己的面试结果&#xff0c;就好像刚破壳的小鹅一样&#xff0c;会把第一眼看到的动物当成自己的母…

走进数字金融峰会,为金融科技数字化赋能

12月20—21日&#xff0c;FSIDigital数字金融峰会在上海圆满召开。本次峰会包含InsurDigital数字保险峰会和B&SDigital数字银行与证券峰会2场平行峰会&#xff1b;吸引了近600位来自保险、银行、证券以及金融科技等行业的领导者和专家齐聚一堂&#xff0c;共同探讨金融业数…

数据库01_增删改查

1、什么是数据&#xff1f;什么是数据库&#xff1f; 数据&#xff1a;描述事物的符号记录称为数据。数据是数据库中存储的基本对象。数据库&#xff1a;存放数据的仓库&#xff0c;数据库中可以保存文本型数据、二进制数据、多媒体数据等数据 2、数据库的发展 第一阶段&…

docker-compose部署kafka

docker-compose.yml配置 version: "3" services:kafka:image: bitnami/kafka:latestports:- 7050:7050environment:- KAFKA_ENABLE_KRAFTyes- KAFKA_CFG_PROCESS_ROLESbroker,controller- KAFKA_CFG_CONTROLLER_LISTENER_NAMESCONTROLLER- KAFKA_CFG_LISTENERSPLAIN…

面向对象设计与分析40讲(15)简单工厂方法模式

文章目录 定义示例优缺点定义 简单工厂模式是一种创建型模式,用于根据客户端的需求创建对象实例,所谓的需求反映到编程语言里就是传入的参数。 简单工厂模式包括三个主要部分: 工厂类(Simple Factory):这是整个模式的核心。它负责根据客户端的请求来创建并返回相应的对…

【教程】从gitee或者github,下载单个文件或文件夹命令

1.打开git 2.初始化 git init 3.设置允许下载子目录 &#xff08;不需要修改任何&#xff0c;只要原样复制&#xff0c;需要按照个人状况修改的话我会标注&#xff09; git config core.sparseCheckout true 4. 选择要下载的单个文件夹的路径 这里单引号内部需要修改&…

Shell 脚本基础

Shell脚本 脚本以#!/bin/bash开头 执行方式 直接使用文件名执行&#xff1a;文件需要执行权限 以bash xxx.sh来执行, 本质上是bash解析器去执行, 文件作为一个输入, 因此可以不需要执行权限 变量 系统变量 自定义变量 定义变量 # 定义一个变量username, 注意不能有多余…

一体机定制_工控触控一体机安卓主板方案

工控一体机是一种集成化的硬件方案&#xff0c;采用了联发科MT8768八核芯片和12nm制程工艺。该芯片拥有2.0GHz的主频和IMG PowerVR GE8320图形处理GPU&#xff0c;具备强大的视频处理能力&#xff0c;并且兼容大部分的视频格式和解码能力。工控一体机搭载了Android 9.0操作系统…

学Java的第二天

一、常量 1.值不可以变化的量。 2. 分类&#xff1a; 字符串常量 用双引号括起来的多个字符&#xff0c;可以包含 0、1 或多个&#xff0c;例如 "a" 、 "abc" 、 " 中国 " 整数常量&#xff0c;例如&#xff1a; -10 、 0 、 88 小数常量&…