当前位置: 首页 > news >正文

【FreeRTOS】调度器挂起与恢复全解析

目录

一、函数基本功能

二、工作原理

三、与临界区的区别

四、返回值说明

五、使用场景

六、使用注意事项

七、示例代码

总结


在 FreeRTOS 中,vTaskSuspendAll()xTaskResumeAll() 是一对用于暂停和恢复任务调度器的函数,主要用于实现 “不可被任务切换打断的操作序列”。它们的核心作用是暂时禁止任务调度,确保一段代码在执行过程中不会被其他任务抢占,同时允许中断正常响应(与临界区不同)。

它们提供了一种机制,允许在不完全禁用中断的情况下保护关键代码段。

一、函数基本功能

  • vTaskSuspendAll(): 暂停 FreeRTOS 任务调度器,使当前运行的任务成为 “唯一活跃的任务”,直到调用 xTaskResumeAll() 恢复调度器为止。 暂停期间,即使有更高优先级的任务就绪,也不会发生任务切换。

  • xTaskResumeAll(): 恢复被 vTaskSuspendAll() 暂停的任务调度器。如果在调度器暂停期间有更高优先级的任务进入就绪态,恢复后会立即触发一次任务切换,让高优先级任务运行。

二、工作原理

FreeRTOS 内部维护一个调度器挂起计数器(scheduler suspension count):

  • 调用 vTaskSuspendAll() 时,计数器加 1,调度器进入 “挂起状态”。

  • 调用 xTaskResumeAll() 时,计数器减 1;当计数器减到 0 时,调度器恢复正常工作。

这种设计支持嵌套调用:例如,可以连续调用 vTaskSuspendAll() 3 次,此时需要连续调用 xTaskResumeAll() 3 次才能完全恢复调度器。

三、与临界区的区别

很多人会混淆 “调度器挂起” 和 “临界区”(taskENTER_CRITICAL()/taskEXIT_CRITICAL()),两者的核心区别在于:

特性调度器挂起(vTaskSuspendAll())临界区(taskENTER_CRITICAL())
中断是否禁用不禁用,中断可正常响应禁用(部分或全部)中断
任务切换是否允许不允许(任务调度被暂停)不允许(中断被禁用,无法触发调度)
适用场景需要允许中断,但禁止任务切换需要禁止中断(如操作硬件寄存器)
对实时性影响较小(中断可响应)较大(中断被阻塞)

注意事项:

  1. 不要长时间挂起调度器:这会严重影响系统的实时性能

  2. 不能在挂起调度器时调用可能导致阻塞的API:如 vTaskDelay(), xQueueReceive()

  3. ISR 仍然可以运行:如果需要保护的数据也被 ISR 访问,应该使用临界区

  4. 嵌套调用必须匹配:确保每次挂起都有对应的恢复调用

四、返回值说明

  • vTaskSuspendAll():无返回值(void)。

  • xTaskResumeAll():返回 BaseType_t 类型:

    • pdTRUE:恢复调度器后,有更高优先级的任务就绪,已触发任务切换。

    • pdFALSE:恢复调度器后,没有更高优先级的任务就绪,当前任务继续运行。

五、使用场景

vTaskSuspendAll()xTaskResumeAll() 适用于需要执行一系列连续、不可被任务切换打断的操作,但又希望允许中断响应的场景。典型例子:

  1. 共享资源的批量操作: 当需要更新多个关联的共享变量(如链表的插入 / 删除多个节点)时,挂起调度器可避免其他任务在操作中途访问资源,导致数据不一致。

  2. 避免短时间内的频繁任务切换: 例如,在执行一个耗时较短(但需要连续执行)的计算任务时,挂起调度器可防止被高优先级任务频繁打断,提高执行效率。

  3. 与中断配合的安全操作: 允许中断响应(如记录中断事件),但暂时不让中断触发的任务切换影响当前操作(直到操作完成后再处理中断触发的任务)。

六、使用注意事项

  1. 禁止调用可能触发任务切换的函数: 在调度器挂起期间,不能调用任何会导致任务阻塞或主动触发任务切换的函数,例如:

    1. vTaskDelay()vTaskDelayUntil()(会让当前任务进入阻塞态,但调度器被挂起,无法切换任务)。

    2. xQueueSend()xQueueReceive() 等队列操作(若队列满 / 空,会触发阻塞,导致死锁)。

    3. taskYIELD()(主动让出 CPU,但调度器被挂起,无法切换任务)。

  2. 挂起时间应尽可能短: 调度器挂起期间,高优先级任务无法得到调度,可能影响系统实时性。因此,需确保挂起的代码段执行时间极短(通常在微秒级)。

  3. 中断中不能调用:(这点很重要!) 这两个函数只能在任务上下文中调用,不能在中断服务程序ISR)中使用(中断中应使用 taskENTER_CRITICAL_FROM_ISR() 等临界区函数)。

中断服务程序(ISR)的设计原则是 “短小精悍”:它是硬件事件触发的紧急处理程序,必须在极短时间内完成(通常微秒级),不能阻塞或长时间占用 CPU。 而 vTaskSuspendAll() 的作用是暂停任务调度器 ,这本质上是一种 “主动阻止任务切换” 的行为 —— 如果在 ISR 中调用它,会导致:

  • 即使 ISR 执行完毕,调度器仍处于挂起状态,高优先级任务无法得到及时调度,严重违反实时系统的响应要求。

  • ISR 本身不能被任务抢占(ISR 优先级高于所有任务),挂起调度器对 ISR 来说毫无意义(ISR 运行时任务本就无法抢占它)。

如果需要在 ISR 中执行不可打断的操作,应使用中断级临界区,而非调度器挂起:

// ISR 中使用临界区保护共享资源
void EXTI_IRQHandler(void) {UBaseType_t uxSavedInterruptStatus;// 进入中断级临界区(禁用部分/全部中断)uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();// 执行需要原子性的操作(如修改共享变量)g_shared_data = new_value;// 退出中断级临界区(恢复中断)taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);// 清理中断标志位EXTI_ClearITPendingBit(EXTI_Line0);
}

4.嵌套调用需配对: 每一次 vTaskSuspendAll() 必须对应一次 xTaskResumeAll(),否则调度器会一直处于挂起状态,导致其他任务无法运行。

七、示例代码

    // 共享资源:一个需要批量更新的链表
    typedef struct Node {int data;struct Node *next;
    } Node_t;Node_t *g_pHead = NULL; // 全局链表头
    QueueHandle_t xQueue;   // 全局队列// 安全地批量更新链表(不希望被其他任务打断)
    void vUpdateListSafely(void) {// 1. 挂起调度器:确保后续操作不会被任务切换打断vTaskSuspendAll();// 2. 执行批量操作(示例:插入3个节点)Node_t *pNode1 = pvPortMalloc(sizeof(Node_t));Node_t *pNode2 = pvPortMalloc(sizeof(Node_t));Node_t *pNode3 = pvPortMalloc(sizeof(Node_t));if (pNode1 && pNode2 && pNode3) {pNode1->data = 1;pNode2->data = 2;pNode3->data = 3;// 插入链表(多步操作,需保证原子性)pNode3->next = g_pHead;pNode2->next = pNode3;pNode1->next = pNode2;g_pHead = pNode1;}// 3. 恢复调度器:如果有高优先级任务就绪,会立即切换BaseType_t xYieldRequired = xTaskResumeAll();// (可选)根据返回值判断是否需要额外处理if (xYieldRequired) {// 已自动完成任务切换,无需额外操作// 此处仅作日志记录configPRINTF(("恢复调度器后已切换到高优先级任务\n"));}
    }

    总结

    vTaskSuspendAll()xTaskResumeAll() 是 FreeRTOS 中用于控制任务调度的重要函数,其核心价值是在允许中断响应的前提下,确保一段代码的原子性执行。使用时需严格遵守注意事项,尤其要避免在挂起期间调用可能触发任务切换的函数,同时控制挂起时间以保证系统实时性。


    文章转载自:

    http://UzGOQVXY.skrcn.cn
    http://5COByxdI.skrcn.cn
    http://WNlSaajW.skrcn.cn
    http://wROVXSyh.skrcn.cn
    http://8C7bg5qe.skrcn.cn
    http://g77C6GuE.skrcn.cn
    http://a28NYNy1.skrcn.cn
    http://DwjFpBk1.skrcn.cn
    http://hs3fhqJT.skrcn.cn
    http://3HYU4OpY.skrcn.cn
    http://V38gloRW.skrcn.cn
    http://EHi1yDES.skrcn.cn
    http://ND2URTSP.skrcn.cn
    http://d4aDxBiR.skrcn.cn
    http://HIVgfTuv.skrcn.cn
    http://qS60NEAM.skrcn.cn
    http://ZtgKuRZK.skrcn.cn
    http://pfbI4vuD.skrcn.cn
    http://fEXn3zNY.skrcn.cn
    http://qG6VupCo.skrcn.cn
    http://ul85YRfz.skrcn.cn
    http://6SNsLj20.skrcn.cn
    http://apwNhRUY.skrcn.cn
    http://Qao7Rn0K.skrcn.cn
    http://30rZa9Eo.skrcn.cn
    http://J4JW8LHx.skrcn.cn
    http://FZxkMJUQ.skrcn.cn
    http://swgs6pqs.skrcn.cn
    http://Dm8HXz49.skrcn.cn
    http://77qlIEia.skrcn.cn
    http://www.dtcms.com/a/388297.html

    相关文章:

  1. 什么是信息安全性测试?如何选择第三方检测机构?
  2. SSM框架——Spring、SpingMVC、Mybatis
  3. MongoDB+cpolar:跨环境数据库管理的无缝方案
  4. Java 泛型详解:从基础到实践
  5. Python与GDAL库进行遥感图像处理:一个完整的实战教程
  6. 构建AI智能体:三十六、决策树的核心机制(二):抽丝剥茧简化专业术语推理最佳分裂点
  7. computeIfAbsent用法讲解
  8. freertos代码结构
  9. C++底层刨析章节一:STL概述与设计哲学:深入理解C++标准模板库的核心
  10. 多态的原理与实现机制
  11. [C++]异常
  12. Windows PE 文件结构详解:从入口到执行的旅程
  13. LLM 处理 PDF 表格的最佳方法:从解析到高效利用
  14. 自动驾驶中的传感器技术50——Radar(11)
  15. WALL-OSS--自变量机器人--2025.9.8--开源
  16. GJOI 9.11/9.13 题解
  17. 基于Spark的用户实时分析
  18. 什么是 Conda 环境?
  19. RK3506开发板QT Creator开发手册,交叉编译工具链与QT应用示例,入门必备
  20. 颠覆3D生成,李飞飞团队新研究实现3D场景「无限探索」,AI构建世界模型能力跨越式进化
  21. 3D 大模型生成虚拟世界
  22. AI技术全景图:从大模型到3D生成,探索人工智能的无限可能
  23. 一天认识一种模型方法--3D人体建模 SMPL
  24. World Labs 的核心技术介绍:生成持久、可导航的 3D 世界
  25. websocket如何推送最新日志
  26. 使用Docker部署bewCloud轻量级Web云存储服务
  27. web Service介绍
  28. Web 架构中的共享存储:NFS 部署与用户压缩
  29. RuoYi整合ZLM4j+WVP
  30. @CrossOrigin的作用