【FreeRTOS】信号量实验-优先级反转

2024-08-30 00:36

本文主要是介绍【FreeRTOS】信号量实验-优先级反转,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 0 前言
  • 0 引言
  • 2 问题复现
    • 2.1 Car1Task
    • 2.2 Car2Task
    • 2.3 Car3Task
  • 4. 互斥量_领导临时提拔你(解决优先级反转)


0 前言

学习视频:
【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 03:06】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=41&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=186

参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》


0 引言

在使用信号量的时候,不论是计数型信号量,还是二进制信号量,都可能会遇到优先级反转的问题。

  • 优先级不反转:高优先级的任务先运行,低优先级的任务后运行。
  • 低优先级任务先运行,高优先级的任务反而不能运行!

什么时候出现这种情况呢?

假设

  1. 最低优先级的任务先Take,先获得了信号量
  2. 中优先级的任务创建出来了,优先级比较高,会一直运行(任务2不去获得信号量)
  3. 最高优先级的任务最后创建,也想获得信号量,但是失败;假设信号量是1,这里就获取不到信号量了

在这里插入图片描述
在这个场景里面,任务3的优先级比任务2的优先级要高,但是任务3被任务2阻塞了,这样就是优先级反转!

2 问题复现

我们现在复现这个问题,第二个任务优先级设置为中等,但是他不去获得信号量,会阻塞第一个任务。导致第一个任务没有办法运行,第一个任务没有办法释放信号量,第三个任务就没有机会运行了

本节源码:在"20_semaphore_binary"的基础上,改出:21_semaphore_priority_inversion,演示优先级反转。

现在没有创建第 21 个程序

依次提高一个优先级,在game2.c的最后面修改代码

    xTaskCreate(Car1Task, "car1", 128, &g_cars[0], osPriorityNormal+1, NULL);xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+2, NULL);xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+3, NULL);	

2.1 Car1Task

static void Car1Task(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);/* 先获取信号量 */xSemaphoreTake(g_xSemTicks, portMAX_DELAY); //不成功就永远等待while (1){/* 读取按键值:读队列 */// xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 */// if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x += 2;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);vTaskDelay(50);if (pcar->x == g_xres - CAR_LENGTH) //移动到最右边{/* 释放信号量 */xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}

2.2 Car2Task

  • 复制第一个任务的函数
  • 第二个任务不去获得信号量,但是要延迟一会~
  • 运行到最右边也不释放信号量
static void Car2Task(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);vTaskDelay(1000);   //1S后运行//    /* 先获取信号量 */
//    xSemaphoreTake(g_xSemTicks, portMAX_DELAY); //不成功就永远等待while (1){/* 读取按键值:读队列 */// xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 */// if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x += 2;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);//                vTaskDelay(50);mdelay(50); //阻塞等待if (pcar->x == g_xres - CAR_LENGTH) //移动到最右边{/* 释放信号量 *///xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}

2.3 Car3Task

任务3的优先级最高,我们修改一下delay,让他最后启动

static void Car3Task(void *params)
{struct car *pcar = params;struct ir_data idata;/* 创建自己的队列 */QueueHandle_t xQueueIR = xQueueCreate(10, sizeof(struct ir_data));/* 注册队列 */RegisterQueueHandle(xQueueIR);/* 显示汽车 */ShowCar(pcar);vTaskDelay(2000);   // 2S后运行/* 先获取信号量 */xSemaphoreTake(g_xSemTicks, portMAX_DELAY); //不成功就永远等待while (1){/* 读取按键值:读队列 */// xQueueReceive(xQueueIR, &idata, portMAX_DELAY);/* 控制汽车往右移动 */// if (idata.val == pcar->control_key){if (pcar->x < g_xres - CAR_LENGTH){/* 隐藏汽车 */HideCar(pcar);/* 调整位置 */pcar->x += 2;if (pcar->x > g_xres - CAR_LENGTH){pcar->x = g_xres - CAR_LENGTH;}/* 重新显示汽车 */ShowCar(pcar);//                vTaskDelay(50);mdelay(50); //阻塞等待if (pcar->x == g_xres - CAR_LENGTH) //移动到最右边{/* 释放信号量 */xSemaphoreGive(g_xSemTicks);vTaskDelete(NULL);}}}}
}

写完这三个任务之后,烧录运行,现象是第一辆车先跑1秒,然后停住,第二辆车跑到终点,之后第一辆车在停止的位置继续前进,第一辆车行驶到终点之后,第三辆车才行驶,到终点停止。

韦老师举了一个日常生活的例子
假设学校机房里有且仅有一台超算,一名学生为了学习,早早来到实验室机房学习了,并且按下指纹进行身份验证,日上三竿,主任带着领导来参观了,主任说你这个太吵了,先停下,超算不许使用了,最后校长来了,校长也是个科研工作者,校长也想使用超算,但是超算已经被学生按下了指纹,抢占了使用权限,没办法校长只能等待,这里就被主任卡住了,校长就非常恼火,这样就是优先级反转的例子。

4. 互斥量_领导临时提拔你(解决优先级反转)

本节源码:在"21_semaphore_priority_inversion"的基础上,改出:22_mutex_priority_inversion

这篇关于【FreeRTOS】信号量实验-优先级反转的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119294

相关文章

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配(Exact Match)2. 正则表达式匹配(Regex Match)3. 前缀匹配(Prefix Match) 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中,location 指令用于定义如何处理特定的请求 URI。由于网站往往需要不同的处理方式来适应各种请求,NGINX 提供了多种匹

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

FreeRTOS学习笔记(六)队列

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、队列的基本内容1.1 队列的引入1.2 FreeRTOS 队列的功能与作用1.3 队列的结构体1.4 队列的使用流程 二、相关API详解2.1 xQueueCreate2.2 xQueueSend2.3 xQueueReceive2.4 xQueueSendFromISR2.5 xQueueRecei

FreeRTOS学习笔记(二)任务基础篇

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、 任务的基本内容1.1 任务的基本特点1.2 任务的状态1.3 任务控制块——任务的“身份证” 二、 任务的实现2.1 定义任务函数2.2 创建任务2.3 启动任务调度器2.4 任务的运行与切换2.4.1 利用延时函数2.4.2 利用中断 2.5 任务的通信与同步2.6 任务的删除2.7 任务的通知2

FreeRTOS学习笔记(四)Freertos的中断管理及临界保护

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Cortex-M 中断管理1.1 中断优先级分组1.2 相关寄存器1.3 相关宏定义1.4 FreeRTOS 开关中断 二、临界段及其保护2.1 taskENTER_CRITICAL( ) 和 taskEXIT_CRITICAL( )2.2 taskENTER_CRITICAL_FROM_ISR( )

61.以太网数据回环实验(4)以太网数据收发器发送模块

(1)状态转移图: (2)IP数据包格式: (3)UDP数据包格式: (4)以太网发送模块代码: module udp_tx(input wire gmii_txc ,input wire reset_n ,input wire tx_start_en , //以太网开始发送信