FreeRTOSBug解析:一个任务printf打印一半突然跳转另一个任务,导致另一个任务无法打印

bug现象:

key任务:

默认任务:

此时两个任务的优先级相同,抢占式调度,时间片轮转,空闲任务让步。

但是会出现一个问题,key任务在发送完队列之后不会立即跳转到默认任务的队列接收函数后的printf,而是会在printf("Send Successfully")打印一半之后再跳转到默认任务的printf里,但是此时默认任务的printf也无法打印了。具体跳转步骤如下图:

原因解析:

步骤“1”执行队列发送函数的时候,会唤醒等待队列数据接收的任务,如果任务优先级比当前任务高,那么会立刻抢占cpu,但是现在由于两个任务的优先级是平级的关系,只能在时间片轮转的时候进行任务调度,让默认任务优先运行(等待队列接收的任务)。

由于不是立即跳转,导致步骤“2”的printf进行了一半,跳转到了步骤“3”的printf,由于printf是不可重入函数,所以步骤“3”的printf无法打印。

运行顺序为:

步骤“1” -> 步骤“2”一半 ->步骤“3” ->步骤“3”所在任务运行一个时间片(等待队列接收)->任务调度回到步骤“2”将printf剩下的一半打印完

解决方法:

1 将key任务优先级抬高

当key任务发送队列唤醒默认任务时,不会在下一个时间片切换任务,而是等key任务主动放弃CPU资源的时候再进行任务调度(先看优先级,再是队列接收),从而保证printf()不会被多个任务同时调用,保证默认任务中步骤“3”printf的打印正常进行。

2 将默认任务优先级抬高

与方法1同理,将默认任务优先级抬高后可以在发送队列结束后立即跳转(默认任务被唤醒,优先级比key任务高,抢占CPU)到默认任务的打印函数(步骤“3”),在默认任务运行到阻塞(等待队列接收)主动出让CPU资源后再跳转到步骤“2”运行printf,这种方法同样保证了printf同时只被一个任务调用。

3 不更改任务优先级,更改调度方式(不常用,了解即可)

关于调度方式:

这个设置的更改即从“时间片轮转”变为“非时间片轮转”,让任务调度只能通过优先级抢占和主动出让CPU资源来进行任务切换。

由于key任务和默认任务优先级平级,当取消时间片轮转后,自然不会出现步骤“2”printf打印一半就被时间片调度跳转到另一个任务的情况,此时没有更高优先级的任务,只有key任务主动出让CPU资源才能切换任务。也保证了printf()函数同时只被一个任务调用。

知识点补充:

立即触发调度的操作/函数:(deepseek)


关于xQueueSend()或xSemaphoreGive()的调度:(deepseek)

1. 默认行为:有条件触发调度

这两个函数在以下场景中可能触发立即调度:

  • 存在等待的高优先级任务:若调用 xQueueSend()xSemaphoreGive() 时,有更高优先级的任务正在等待队列数据或信号量,则调度器会立即切换任务(抢占式调度生效)。例如:

    // 任务 A(低优先级)发送数据到队列

  • xQueueSend(xQueue, &data, 0);

  • // 任务 B(高优先级)正在阻塞等待该队列

  • // 此时任务 A 会立即被抢占,任务 B 开始运行

    • 配置为抢占式调度:若 FreeRTOS 的调度策略设置为抢占式(默认),则高优先级任务就绪时会立即触发切换。


    2. 不触发调度的场景

    以下情况不会立即触发调度:

    • 无等待任务或优先级不满足:若没有任务在等待队列/信号量,或等待的任务优先级不高于当前任务,则当前任务继续运行,调度器不会切换。

      • 中断服务程序ISR)中未显式请求调度:在 ISR 中调用 xQueueSendFromISR()xSemaphoreGiveFromISR() 时,需手动调用 portYIELD_FROM_ISR() 才会触发调度。


        3. 与时间片轮转调度的关系

        若系统启用了时间片轮转(configUSE_TIME_SLICING = 1),即使没有更高优先级任务,同优先级任务也会在时间片用完时切换。但 xQueueSend()xSemaphoreGive() 本身不会强制切换,需依赖时间片中断。


        总结

        场景

        是否触发调度

        说明

        发送数据/信号量唤醒更高优先级任务

        立即触发

        抢占式调度生效,任务切换

        发送数据/信号量唤醒同或低优先级任务

        不触发

        当前任务继续运行

        在 ISR 中未手动调用 portYIELD_FROM_ISR()

        不触发

        需显式请求调度

        无任务等待队列/信号量

        不触发

        无任务需切换

        补充:显式调度与隐式调度(deepseek)
        1. 显式触发调度(Explicit Scheduling)

        含义

        开发者主动调用特定函数来强制触发任务调度,明确要求系统立即切换到其他就绪任务。

        特点

        • 直接控制调度时机,代码中可见明确的调度指令。

        • 适用于需要手动让出CPU的场景,例如协作式多任务或优化关键代码段的执行顺序。

        FreeRTOS 中的显式触发函数

        • taskYIELD():立即触发调度器重新选择任务,即使当前任务的时间片未用完。

          void vTaskFunction(void *pvParameters) { while (1) { // 执行某些操作后主动让出CPU taskYIELD(); // 显式触发调度 } }


          2. 隐式触发调度(Implicit Scheduling)

          含义

          调度由系统事件或API函数的副作用自动触发,开发者无需显式调用调度函数。

          特点

          • 调度时机由系统管理,与任务状态变化(如阻塞、资源释放)或中断事件绑定。

          • 更符合抢占式调度机制,提升系统自动化程度。

          FreeRTOS 中的隐式触发场景

          • 任务阻塞操作:调用 vTaskDelay()xQueueReceive() 等函数时,任务进入阻塞状态,调度器自动切换任务。

            vTaskDelay(pdMS_TO_TICKS(100)); // 隐式触发调度(任务阻塞)

            • 资源释放唤醒高优先级任务:例如,在 xSemaphoreGive()xQueueSend() 中,若释放资源后存在更高优先级任务就绪,调度器自动抢占当前任务。

              xSemaphoreGive(xSemaphore); // 隐式触发调度(若唤醒高优先级任务)

              • 中断服务程序ISR)中的调度请求:在中断中调用 xQueueSendFromISR()xSemaphoreGiveFromISR() 后,通过 portYIELD_FROM_ISR() 隐式触发调度(需结合中断上下文)。


              对比总结

              特性

              显式触发调度

              隐式触发调度

              触发方式

              开发者主动调用函数(如 taskYIELD()

              系统自动触发(如任务阻塞、资源释放、中断)

              控制权

              完全由开发者控制

              由系统事件或API逻辑控制

              典型场景

              协作式任务切换、优化执行顺序

              抢占式调度、事件驱动任务切换

              代码可见性

              显式代码指令

              隐含在API或系统行为中


              不可重入函数

              顾名思义:不可重入函数就是不可以在它还没有返回就再次被调用。

              不可重入原因:

              由于函数内部使用了全局变量、静态变量、或调用了不可重入的函数等,导致函数在执行过程中可能被中断,并在中断后继续执行时出现数据错误或不可预料的后果。以下是不可重入函数的几个特点:

              1. 使用全局变量或静态变量:如果函数内部使用了全局变量或静态变量,那么在多任务环境下,多个任务同时调用该函数时,可能会对这些共享变量进行并发修改,导致数据不一致。

              2. 调用不可重入函数:如果函数内部调用了其他不可重入函数,那么这些被调用的函数也可能因为上述原因导致整个调用链不可重入。

              3. 使用动态内存分配:函数体内调用了malloc()或者free()函数,由于这些函数维护内部的链表,且这个过程不是原子的,因此在多任务环境下可能导致内存管理出现问题。

              4. 使用标准I/O函数:标准I/O函数通常使用全局数据结构,因此在多任务环境下也可能导致不可重入问题。

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

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

              相关文章

              操作系统八股文整理(一)

              操作系统八股文整理 一、进程和线程的区别二、进程与线程的切换过程一、进程切换进程切换的步骤: 二、线程切换线程切换的步骤: 三、进程切换与线程切换的对比四、上下文切换的优化 三、系统调用一、系统调用的触发二、从用户空间切换到内核空间三、执行…

              卷积神经网络(CNN)之 EfficientNet

              在深度学习领域,模型的计算效率与性能之间的平衡一直是一个核心挑战。随着卷积神经网络(CNN)在图像分类、目标检测等任务中取得显著成果,模型的复杂度和计算需求也急剧增加。2019年,Google Research 提出的 EfficientN…

              学生选课管理系统数据库设计报告

              学生选课管理系统数据库设计报告 一、需求分析 (一)项目背景 学生选课管理系统是高校教学管理的重要组成部分,旨在实现学生选课、课程管理、成绩录入与查询等功能的自动化和信息化。通过该系统,学生可以方便地选择课程&#xf…

              工具介绍《Awsome-Redis-Rogue-Server 与 redis-rogue-server》

              1. 核心功能与攻击场景 redis-rogue-server 基于Redis主从复制漏洞(CVE未公开),针对Redis 4.x~5.0.5版本设计,通过伪造恶意主节点强制同步恶意模块(.so文件)实现远程代码执行(RCE)。…

              Razor C# 变量

              Razor C# 变量 引言 在ASP.NET MVC和Razor视图引擎中,变量是构建动态网页的基础。理解Razor C#变量的使用对于开发者来说至关重要。本文将详细介绍Razor C#变量的概念、类型、作用域以及如何在实际项目中有效使用它们。 一、Razor C# 变量的概念 Razor C# 变量是存储在Raz…

              【QA】模板方法模式在Qt中有哪些应用?

              在 Qt 框架中,模板方法模式(Template Method Pattern)被广泛应用于框架的设计中,通过定义算法骨架并允许子类在不改变结构的情况下重写部分步骤。以下是 Qt 中典型的应用场景及示例: 1. 事件处理(Event Ha…

              有趣的算法实践:整数反转与回文检测(Java实现)

              题目描述:整数反转与回文检测 要求实现两个功能: 将输入的整数反转(保留符号,如输入-123返回-321)判断反转后的数是否为回文数(正反读相同) 示例: 输入:123 → 反转结…

              【协作开发】低成本一键复刻github的gitea

              在阅读 next-public 时,反思原本的需求,是否本未倒置,故而重新调研当下开源现状。发现 gitea 完全满足商业软件的开发要求,并且价格足够低,使用足够方便,其他同类软件完全不用看了,真是世界级的…

              基于“动手学强化学习”的知识点(二):第 15 章 模仿学习(gym版本 >= 0.26)

              第 15 章 模仿学习(gym版本 > 0.26) 摘要 摘要 本系列知识点讲解基于动手学强化学习中的内容进行详细的疑难点分析!具体内容请阅读动手学强化学习! 对应动手学强化学习——模仿学习 # -*- coding: utf-8 -*-import gy…

              JAVA面试_进阶部分_Java JVM:垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

              在什么时候: 首先需要知道,GC又分为minor GC 和 Full GC(major GC)。Java堆内存分为新生代和老年代,新生代 中又分为1个eden区和两个Survior区域。 一般情况下,新创建的对象都会被分配到eden区&#xff…

              2024年消费者权益数据分析

              📅 2024年315消费者权益数据分析 数据见:https://mp.weixin.qq.com/s/eV5GoionxhGpw7PunhOVnQ 一、引言 在数字化时代,消费者维权数据对于市场监管、商家诚信和行业发展具有重要价值。本文基于 2024年315平台线上投诉数据,采用数…

              设计模式Python版 访问者模式

              文章目录 前言一、访问者模式二、访问者模式示例 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式:关注类和对象之间的组…

              安全无事故连续天数计算,python 时间工具的高效利用

              安全天数计算,数据系统时间直取,安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建,本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值:在于输出思考与经验&am…

              大型语言模型(LLM)部署中的内存消耗计算

              在部署大型语言模型(LLM)时,显存(VRAM)的合理规划是决定模型能否高效运行的核心问题。本文将通过详细的公式推导和示例计算,系统解析模型权重、键值缓存(KV Cache)、激活内存及额外开…

              Mysql表的查询

              一:创建一个新的数据库(companydb),并查看数据库。 二:使用该数据库,并创建表worker。 mysql> use companydb;mysql> CREATE TABLE worker(-> 部门号 INT(11) NOT NULL,-> 职工号 INT(11) NOT NULL,-> 工作时间 D…

              ASP.NET Webform和ASP.NET MVC 后台开发 大概80%常用技术

              本文涉及ASP.NET Webform和ASP.NET MVC 后台开发大概80%技术 2019年以前对标 深圳22K左右 广州18K左右 武汉16K左右 那么有人问了2019年以后的呢? 答:吉祥三宝。。。 So 想继续看下文的 得有自己的独立判断能力。 C#.NET高级笔试题 架构 优化 性能提…

              首页性能优化

              首页性能提升是前端优化中的核心任务之一,因为首页是用户访问的第一入口,其加载速度和交互体验直接影响用户的留存率和转化率。 1. 性能瓶颈分析 在优化之前,首先需要通过工具分析首页的性能瓶颈。常用的工具包括: Chrome DevTo…

              一周学会Flask3 Python Web开发-SQLAlchemy删除数据操作-班级模块

              锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 首页list.html里加上删除链接&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta c…

              改变一生的思维模型【12】笛卡尔思维模型

              目录 基本结构 警惕认知暗礁 案例分析应用 一、怀疑阶段:破除惯性认知 二、解析阶段:拆解问题为最小单元 三、整合阶段:重构逻辑链条 四、检验阶段:多维验证解决方案 总结与启示 笛卡尔说,唯独自己的思考是可以相信的。 世界上所有的事情,都是值得被怀疑的,但是…

              需求文档(PRD,Product Requirement Document)的基本要求和案例参考:功能清单、流程图、原型图、逻辑能力和表达能力

              文章目录 引言I 需求文档的基本要求结构清晰内容完整语言准确图文结合版本管理II 需求文档案例参考案例1:电商平台“商品中心”功能需求(简化版)案例2:教育类APP“记忆宝盒”非功能需求**案例3:软件项目的功能需求模板3.1 功能需求III 需求文档撰写技巧1. **从核心逻辑出发…