【FreeRTOS进阶】优先级翻转现象详解及解决方案
【FreeRTOS进阶】优先级翻转现象详解及解决方案
接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现,尤其是在任务较多的场景下,而且问题定位起来比较麻烦。
什么是优先级翻转?
优先级翻转的核心定义很简单:较低优先级的任务阻塞了较高优先级任务的执行。
有同学可能会觉得奇怪:"这不对啊,不是应该高优先级任务抢占低优先级任务吗?怎么会反过来呢?“没错,这确实与我们对抢占式调度的认知相反,所以它被称为"优先级翻转”。
优先级翻转的发生过程
我们通过一个具体场景来详细分析这个问题:
假设我们现在有三个任务,分别是Task A(高优先级)、Task C(中优先级)和Task B(低优先级),以及一个信号量S。
事件顺序如下:
- 低优先级的Task B先运行,并获取了信号量S
- 高优先级的Task A变为就绪状态,抢占了Task B开始执行
- Task A也需要获取信号量S,但此时信号量已被Task B持有
- Task A进入阻塞状态,等待信号量释放
- 控制权回到Task B,Task B继续执行
- 中优先级的Task C变为就绪状态,抢占了低优先级的Task B
- 由于Task B未释放信号量,Task A继续阻塞
- Task C可以不受干扰地执行完毕(类似"山中无老虎,猴子称大王")
- Task C执行完后,控制权回到Task B
- Task B最终执行到释放信号量S的代码
- 信号量释放后,Task A获得信号量并恢复执行
这整个过程中,我们可以看到高优先级任务A被低优先级任务B阻塞了,而且这个阻塞时间不仅取决于B,还包括所有优先级介于A和B之间的任务(如Task C)的执行时间。
优先级翻转的危害
这个问题的危害在于:
- 实时性丧失:高优先级任务无法在预期时间内执行完成
- 系统行为不可预测:高优先级任务的阻塞时间取决于中低优先级任务的执行时间
- 可能导致严重后果:在关键实时系统中(如航空、医疗设备),这可能导致灾难性后果
解决优先级翻转的方法
FreeRTOS提供了三种主要解决优先级翻转问题的机制:
1. 优先级继承(Priority Inheritance)
这是最常用的解决方案:当低优先级任务持有高优先级任务需要的资源时,低优先级任务临时"继承"高优先级任务的优先级,直到释放资源。
在FreeRTOS中,互斥量(Mutex)默认启用优先级继承机制:
/* 创建带有优先级继承的互斥量 */
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();/* 使用互斥量进行资源保护 */
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {/* 临界区代码 */xSemaphoreGive(xMutex);
}
2. 优先级天花板协议(Priority Ceiling Protocol)
为每个共享资源设置一个"天花板优先级",任何访问该资源的任务都会临时提升到该优先级,直到释放资源。
/* 定义天花板优先级并创建互斥量 */
#define CEILING_PRIORITY (configMAX_PRIORITIES - 1)
SemaphoreHandle_t xMutex;/* 创建普通互斥量 */
xMutex = xSemaphoreCreateMutex();/* 使用前先将优先级提升到天花板优先级 */
UBaseType_t uxOriginalPriority = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, CEILING_PRIORITY);/* 使用互斥量进行资源保护 */
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {/* 临界区代码 */xSemaphoreGive(xMutex);
}/* 恢复原始优先级 */
vTaskPrioritySet(NULL, uxOriginalPriority);
3. 关闭中断或调度器
对于短小的临界区,可以通过临时关闭中断或调度器来避免优先级翻转:
/* 方法1:关闭中断 */
taskENTER_CRITICAL();
/* 临界区代码(必须非常短) */
taskEXIT_CRITICAL();/* 方法2:挂起调度器 */
vTaskSuspendAll();
/* 临界区代码 */
xTaskResumeAll();
实际案例:Mars Pathfinder任务重启事件
NASA的火星探路者任务在1997年就遇到了由于优先级翻转导致的系统重启问题。系统检测到了"长时间任务"而触发保护性重启。调查发现,这是由于优先级翻转导致高优先级任务被阻塞太久。幸运的是,VxWorks操作系统支持优先级继承,工程师远程启用了这一功能,问题得到解决。
这个经典案例说明了优先级翻转在实际中的危害及其解决方案的重要性。
如何避免优先级翻转?
- 尽量减少共享资源:降低任务间的资源依赖
- 合理设计优先级:相互有资源依赖的任务尽量使用接近的优先级
- 使用优先级继承的互斥量:替代普通信号量进行资源保护
- 缩小临界区范围:尽量减少持有互斥量的时间
- 避免嵌套锁:防止死锁和复杂的优先级问题
实验验证
我们可以通过一个简单实验来模拟优先级翻转现象:
/* 创建不带优先级继承的二值信号量 */
SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xSemaphore); /* 初始状态为可用 *//* 低优先级任务 */
void vLowPriorityTask(void *pvParameters) {for(;;) {/* 获取信号量 */if(xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {printf("低优先级任务获取信号量\r\n");/* 模拟长时间处理 */vTaskDelay(pdMS_TO_TICKS(2000));printf("低优先级任务释放信号量\r\n");xSemaphoreGive(xSemaphore);}vTaskDelay(pdMS_TO_TICKS(1000));}
}/* 中优先级任务 */
void vMediumPriorityTask(void *pvParameters) {for(;;) {printf("中优先级任务运行\r\n");/* 模拟占用CPU时间 */vTaskDelay(pdMS_TO_TICKS(3000));}
}/* 高优先级任务 */
void vHighPriorityTask(void *pvParameters) {for(;;) {printf("高优先级任务尝试获取信号量\r\n");TickType_t xStartTime = xTaskGetTickCount();if(xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {TickType_t xEndTime = xTaskGetTickCount();printf("高优先级任务获取信号量,等待时间: %lu ms\r\n", (xEndTime - xStartTime) * portTICK_PERIOD_MS);vTaskDelay(pdMS_TO_TICKS(500));xSemaphoreGive(xSemaphore);}vTaskDelay(pdMS_TO_TICKS(5000));}
}/* 创建任务 */
void vStartTasks(void) {xTaskCreate(vLowPriorityTask, "LowTask", configMINIMAL_STACK_SIZE, NULL, 1, NULL);xTaskCreate(vMediumPriorityTask, "MedTask", configMINIMAL_STACK_SIZE, NULL, 2, NULL);xTaskCreate(vHighPriorityTask, "HighTask", configMINIMAL_STACK_SIZE, NULL, 3, NULL);
}
通过这个实验,我们能观察到高优先级任务需要等待低优先级任务释放信号量,而这个过程又被中优先级任务"插队",导致高优先级任务的等待时间大大延长。
总结
优先级翻转是实时系统设计中容易被忽视但又非常重要的问题。合理使用互斥量和优先级继承机制是解决此问题的关键。在设计多任务实时系统时,我必须谨慎思考任务间的资源共享和优先级分配。
如果你想深入学习FreeRTOS的更多高级特性及实战案例,欢迎访问我的GitHub仓库:Despacito0o/FreeRTOS,那里有从入门到精通的完整教程和示例代码,包括本文讨论的优先级翻转问题的实战解决方案!
📚 FreeRTOS + STM32学习资源库 - 从入门到精通的完整学习路径
相关推荐阅读:
- FreeRTOS二值信号量详解与实战教程
- FreeRTOS计数型信号量详解与实战教程
相关文章:
【FreeRTOS进阶】优先级翻转现象详解及解决方案
【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现,尤其是在任务较多的场景下,而且问题定位起来比较麻烦。 什么是优先级翻转? 优先级翻转的核心定义很简单:…...
结合建筑业务讲述TOGAF标准处理哪种架构
TOGAF标准处理哪种架构 内容介绍业务架构业务策略,治理,组织和关键业务流程数据架构组织的逻辑和物理数据资产以及数据管理资源的结构应用架构待部署的各个应用程序,它们之间的交互以及与组织核心业务流程的关系的蓝图技术架构支持业务&#…...
C++入门小馆: 深入string类(一)
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…...
NHANES指标推荐:WWI
文章题目:Weight-adjusted waist circumference index with hepatic steatosis and fibrosis in adult females: a cross-sectional, nationally representative study (NHANES 2017-2020) DOI:10.1186/s12876-025-03706-4 中文标题:体重调整…...
2025.04.18|【Map】地图绘图技巧全解
Add circles Add circles on a Leaflet map Change tile Several background tiles are offered by leaflet. Learn how to load them, and check the possibilities. 文章目录 Add circlesChange tile 2025.04.18【Map】| 地图绘图技巧全解1. 准备工作2. 地理区域着色图&…...
PR第一课
目录 1.新建 2.PR内部设置 3.导入素材 4.关于素材窗口 5.关于编辑窗口 6.序列的创建 7.视频、图片、音乐 7.1 带有透明通道的素材 8.导出作品 8.1 打开方法 8.2 导出时,需要修改的参数 1.新建 2.PR内部设置 随意点开 编辑->首选项 中的任意内容&a…...
C# 预定义类型全解析
在 C# 编程中,预定义类型是基础且重要的概念。下面我们来详细了解 C# 的预定义类型。 预定义类型概述 C# 提供了 16 种预定义类型,包含 13 种简单类型和 3 种非简单类型。所有预定义类型的名称都由全小写字母组成。 预定义简单类型 预定义简单类型表…...
@EnableAsync+@Async源码学习笔记之六
接上文,我们本文分析 AsyncExecutionAspectSupport 的源码: package org.springframework.aop.interceptor;import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFu…...
Java CMS和G1垃圾回收器
举个真带劲的例子:把JVM内存比作你家的祖传旱厕 想象你有个祖传旱厕,分三个坑: 新坑区(年轻代):刚拉的屎热乎着(新对象)陈年坑(老年代):风干的屎…...
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
效果图: message.vue 消息组件 子组件 <template><div class"custom-notification"><div class"content"><span click"gotoMessageList(currentMessage.split()[1])">{{ currentMessage.split()[0] }}</…...
不规则曲面上两点距离求取
背景 在CT中求皮肤上两点间的弧长。由于人体表面并不是规则的曲面,不可能用圆的弧长求取方法来计算出两点间的弧长。 而在不规则的曲面上求两点的距离,都可以用类似测地线距离求取的方式来求取(积分),而转化为搜索路…...
Redis面试问题缓存相关详解
Redis面试问题缓存相关详解 一、缓存三兄弟(穿透、击穿、雪崩) 1. 穿透 问题描述: 缓存穿透是指查询一个数据库中不存在的数据,由于缓存不会保存这样的数据,每次都会穿透到数据库,导致数据库压力增大。例…...
性能比拼: Elixir vs Go
本内容是对知名性能评测博主 Anton Putra Elixir vs Go (Golang) Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 对比 Elixir 和 Go 简介 许多人长期以来一直要求我对比 Elixir 和 Go。在本视频…...
精益数据分析(6/126):深入理解精益分析的核心要点
精益数据分析(6/126):深入理解精益分析的核心要点 在创业和数据驱动的时代浪潮中,我们都在不断探索如何更好地利用数据推动业务发展。我希望通过和大家分享对《精益数据分析》的学习心得,一起在这个充满挑战和机遇的领…...
【Linux网络与网络编程】11.数据链路层mac帧协议ARP协议
前面在介绍网络层时我们提出来过一个问题:主机是怎么把数据交给路由器的?那里我们说这是由数据链路层来做的。 网络上的报文在物理结构上是以mac帧的形式流动的,但在逻辑上是以IP流动的,IP的流动是需要mac帧支持的。 数据链路层解…...
JAVA设计模式:注解+模板+接口
1.基础组件 1.1注解类控制代码执行启动、停止、顺序 /*** author : test* description : 数据同步注解* date : 2025/4/18*/ Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented public interface SyncMeta {/*** 执行服务名称* return*/String name…...
Linux系统编程 day6 进程间通信mmap
父子共享的信息:文件描述符,mmap建立的共享映射区(MAP_SHARED) mmap父子间进程通信 var的时候 :读时共享,写时复制 父进程先创建映射区,指定共享MAP_SHARED权限 , fork创建子进程…...
【MySQL】MySQL建立索引不知道注意什么?
基本原则: 1.选择性原则: 选择高选择性的列建立索引(该列有大量不同的值) 2.适度原则:不是越多越好,每个索引都会增加写入开销 列选择注意事项: 1.常用查询条件列:WHERE字句中频繁使用的列 2.连接操作列…...
定制一款国密浏览器(9):SM4 对称加密算法
上一章介绍了 SM3 算法的移植要点,本章介绍对称加密算法 SM4 的移植要点。 SM4 算法相对 SM3 算法来说复杂一些,但还是比较简单的算法,详细算法说明参考《GMT 0002-2012 SM4分组密码算法》这份文档。铜锁开源项目的实现代码在 sm4.c 文件中,直接拿过来编译就可以。 但需要…...
Redis 的持久化机制(RDB, AOF)对微服务的数据一致性和恢复性有何影响?如何选择?
Redis 的持久化机制(RDB 和 AOF)对于保证 Redis 服务重启或崩溃后数据的恢复至关重要,这直接影响到依赖 Redis 的微服务的数据一致性和恢复能力。 1. RDB (Redis Database Backup) 机制: 在指定的时间间隔内,将 Redis 在内存中的…...
lottie深入玩法
A、json文件和图片资源分开 delete 是json资源名字 /res/lottie/delete_anim_images是图片资源文件夹路径 JSON 中引用的图片名,必须与实际图片文件名一致 B、json文件和图片资源分开,并且图片加载不固定 比如我有7张图片,分别命名1~7&…...
Android学习总结之算法篇七(图和矩阵)
有向图的深度优先搜索(DFS)和广度优先搜索(BFS)的示例,以此来模拟遍历 GC Root 引用链这种有向图结构: 一、深度优先搜索(DFS) import java.util.*;public class GraphDFS {privat…...
docker 大模型
使用 Docker 实现大模型的步骤指南 在今天的文章中,我们将指导刚入行的小白如何使用 Docker 来运行大模型。Docker 是一个开放源代码的平台,允许开发者自动化应用程序的部署、扩展和管理。通过将大模型放入 Docker 容器中,我们可以确保其在各…...
热门与冷门并存,25西电—电子工程学院(考研录取情况)
1、电子工程学院各个方向 2、电子工程学院近三年复试分数线对比 学长、学姐分析 由表可看出: 1、电子科学与技术25年相较于24年上升20分 2、信息与通信工程、控制科学与工程、新一代电子信息技术(专硕)25年相较于24年下降25分 3、25vs24推…...
Warcraft Logs [Classic] [WCL] BOSS ID query
Warcraft Logs [Classic] [WCL] BOSS ID query 所有副本BOSSID查询 https://wowpedia.fandom.com/wiki/DungeonEncounterID#Retail IDNameMapInstanceIDPatch227High Interrogator GerstahnBlackrock Depths230228Lord RoccorBlackrock Depths230229Houndmaster GrebmarBlackro…...
python录屏工具实现
python录屏工具实现 实现一 按Ctrl+Shift+8开始录制,按Ctrl+Shift+9结束录制,视频保存到“ d:\录屏视频”目录中。 先看用了哪些库 import cv2: 引入 OpenCV 库,这是一个开源计算机视觉库,用于图像和视频处理。在这个程序中,它用于创建视频文件、处理图像等。需要安装ope…...
架构师面试(三十一):IM 消息收发逻辑
问题 今天聊一下 IM 系统最核心的业务逻辑。 在上一篇短文《架构师面试(三十):IM 分层架构》中详细分析过,IM 水平分层架构包括:【入口网关层】、【业务逻辑层】、【路由层】和【数据访问层】;除此之外&a…...
基于若依框架前后端分离的项目部署
文章目录 单项目的部署项目目录后端打包上传前端打包上传配置nginx服务器打开防火墙完成 两个项目的部署两个项目介绍后端打包并上传前端打包并上传nginx配置服务器端口开放完成 腾讯云服务器 之 环境搭建 单项目的部署 项目目录 后端打包上传 查看端口号 在ruoyi-admin的appl…...
黑马Java基础笔记-1
JVM,JDK和JRE JDK是java的开发环境 JVM虚拟机:Java程序运行的地方 核心类库:Java已经写好的东西,我们可以直接用。 System.out.print中的这些方法就是核心库中的所包含的 开发工具: javac(编译工具)、java&…...
面向新一代扩展现实(XR)应用的物联网框架
中文标题: 面向新一代扩展现实(XR)应用的物联网框架 英文标题: Towards an IoT Framework for the New Generation of XR Applications 作者信息 Joo A. Dias,UNIDCOM - IADE,欧洲大学,里斯本&…...
