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 退出时请求切换 某些端口使用;查看项目移植层,不要和其他端口宏混用
相关推荐
中文英文-我选中文4 天前
8.2 学习探索
freertos
qq_401700419 天前
FreeRTOS 堆内存管理源码剖析:释放一块内存,空闲块数量反而变少了?
单片机·freertos
List<String> error_P11 天前
使用FreeRTOS搭建工程项目(一)
freertos
@嵌入式扫地僧19 天前
RTOS 面试实战:任务状态切换原理
freertos·rtos·时间片轮转·任务状态切换·抢占式调度
WWJA王文举1 个月前
Bootloader、App 和升级分区之间是什么关系?
stm32·freertos·嵌入式开发·在线升级
怀民民民1 个月前
嵌入式软件学习路线与项目实践记录:STM32、FreeRTOS、Linux 与 OpenMV
stm32·freertos·嵌入式软件·嵌入式linux·openmv
长江后浪博客1 个月前
ESP32-SOLO-1 在 ESP-IDF v5.5 下启动不断重启问题分析与解决
单片机·嵌入式硬件·嵌入式·esp32·freertos·esp-idf
WWJA王文举1 个月前
FreeRTOS动态创建任务和静态创建任务详解:xTaskCreate与xTaskCreateStatic区别
操作系统·freertos·嵌入式开发
离凌寒2 个月前
一、关于rtthread nano使用上的细节总结
freertos