FreeRTOS任务挂起和恢复

FreeRTOS 任务挂起与恢复

1. 先理解"挂起"

把任务想成正在工作的程序:挂起 就像按下暂停键,任务暂时不再参与调度;恢复则让它重新具备运行资格。任务恢复后不一定立刻运行,还要看其他就绪任务的优先级。

状态/操作 通俗理解 如何继续
就绪 任务准备好,等待 CPU 等调度器选择
阻塞 任务在等时间或某个条件 等待时间到或条件满足,内核自动解除阻塞
挂起 任务被明确暂停 需要其他代码调用恢复 API
删除 任务已被移除 不能恢复;需要重新创建任务

阻塞和挂起最大的区别是:阻塞通常由等待行为产生,并可由等待条件自动解除;挂起是显式暂停,必须显式恢复。

2. 挂起任务:vTaskSuspend()

中文含义

挂起指定任务,使它从调度相关列表中移除并进入挂起状态。在恢复之前,该任务不会被调度运行。

复制代码
void vTaskSuspend(TaskHandle_t xTaskToSuspend);

参数 xTaskToSuspend 是目标任务句柄。传入 NULL 表示挂起当前任务:

复制代码
vTaskSuspend(taskHandle);  /* 挂起指定任务 */
vTaskSuspend(NULL);        /* 挂起当前任务 */

当前任务挂起后,调度器会切换到其他可运行任务。之后必须有别的任务或中断恢复它,否则它会一直保持挂起状态。

3. 在任务中恢复:vTaskResume()

中文含义

恢复之前通过 vTaskSuspend() 挂起的任务,使其重新进入就绪状态。

复制代码
void vTaskResume(TaskHandle_t xTaskToResume);

该函数应在任务上下文中调用,不能在中断服务函数中调用:

复制代码
vTaskResume(taskHandle);  /* 恢复指定的挂起任务 */

调用后,目标任务成为就绪任务。如果它的优先级高于当前任务,调度器可能立即切换到它;否则它会等待以后获得 CPU。

恢复函数只对当前处于挂起状态的任务起作用。若任务当时并没有挂起,调用不会替它保存一个"以后再恢复"的请求。

4. 在中断中恢复:xTaskResumeFromISR()

中文含义

在中断服务函数(ISR)中恢复一个挂起任务。

复制代码
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);

该函数只能用于中断上下文。它返回是否需要在中断退出时进行任务切换:

返回值 含义
pdTRUE 恢复后需要请求任务切换;应在中断退出前使用端口提供的 ISR 切换宏处理
pdFALSE 当前不需要因此进行任务切换

典型结构如下,具体切换宏以所用 FreeRTOS 移植层为准:

复制代码
void EXTI_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
​
    /* 清除或处理外部中断标志 */
    xHigherPriorityTaskWoken = xTaskResumeFromISR(taskHandle);
​
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

一个重要限制:恢复不是事件通知

xTaskResumeFromISR() 不会把"恢复请求"排队保存。如果中断发生时任务尚未挂起,这次调用不会留到任务以后挂起时再生效。于是可能出现:先发生中断恢复,后执行任务挂起,任务最终一直挂起的时序问题。

如果需求是"无论任务是否正在等待,都要记住这个事件",通常应考虑任务通知、信号量或队列,而不是把挂起/恢复当作事件传递机制。

5. 挂起和恢复的典型流程

例如,一个控制任务暂时不需要运行:

复制代码
控制任务正在运行
    ↓ vTaskSuspend(taskHandle)
控制任务进入挂起状态,不再参与调度
    ↓ 某条件满足,其他任务调用 vTaskResume(taskHandle)
控制任务回到就绪状态
    ↓ 调度器按优先级选择任务
控制任务获得 CPU 后从被挂起的位置继续执行

如果恢复动作来自中断,则把 vTaskResume() 换成 xTaskResumeFromISR(),并根据返回值在中断退出时请求切换。

6. 配置要求与使用注意

启用任务挂起/恢复 API,需要在 FreeRTOSConfig.h 中配置:

复制代码
#define INCLUDE_vTaskSuspend 1

启用中断中恢复 API,还需要:

复制代码
#define INCLUDE_xTaskResumeFromISR 1

要点:

  1. vTaskSuspend() 可以挂起自身;传入 NULL 即可。自挂起后,任务要等其他上下文恢复才能继续。

  2. vTaskResume() 用于任务上下文;xTaskResumeFromISR() 用于 ISR,不能混用。

  3. 挂起不是嵌套计数操作。无论对同一任务调用多少次 vTaskSuspend(),一次有效恢复就可以解除挂起。

  4. 任务恢复只表示进入就绪状态,不保证立即运行;调度器仍按优先级和配置决定执行顺序。

  5. 挂起任务不会释放它的栈或 TCB,也不会像删除任务那样销毁任务。

  6. 如果要可靠地传递中断事件或累计多次事件,不要依赖恢复调用;改用任务通知、信号量或队列等同步机制。

7. 本章函数汇总

函数 中文含义 主要用法
vTaskSuspend() 挂起任务 vTaskSuspend(handle) 挂起指定任务;vTaskSuspend(NULL) 挂起当前任务
vTaskResume() 在任务中恢复挂起任务 vTaskResume(handle);只能在任务上下文调用,且目标任务应处于挂起状态
xTaskResumeFromISR() 在中断中恢复挂起任务 xTaskResumeFromISR(handle);根据返回值决定是否在 ISR 退出时请求切换
portYIELD_FROM_ISR()(移植宏) 在 ISR 退出时请求任务切换 通常传入 xTaskResumeFromISR() 的返回状态;具体名称依端口而异
portEND_SWITCHING_ISR()(部分端口宏) 根据条件在 ISR 退出时请求切换 某些端口使用;查看项目移植层,不要和其他端口宏混用
相关推荐
La Pulga1 天前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga2 天前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga4 天前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
La Pulga4 天前
【FreeRTOS源码学习笔记】任务创建
笔记·stm32·mcu·学习·嵌入式·freertos·实时操作系统
天吾cc9 天前
FreeRTOS 事件标志组
freertos
编码追梦人11 天前
基于 ESP32-S3 的便携式多参数环境监护仪设计:从硬件选型到 FreeRTOS 多任务落地
数据库·单片机·嵌入式硬件·mongodb·esp32·freertos·低功耗设计
天吾cc14 天前
FreeRTOS 队列集
freertos
硅基手札14 天前
操作系统设计思想系列 06 · FreeRTOS 深度 — 极简可移植范本
架构·操作系统·freertos·实时操作系统·嵌入式开发·设计思想
天吾cc19 天前
FreeRTOS 列表和列表项
freertos
qq_4017004120 天前
RTOS址与值 void*任务传参的坑 走void*万能转的天坑
freertos