FreeRTOS(1)——动态任务创建和删除
本文最后更新于20 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

FreeRTOS 中创建任务,本质上就是创建一个任务控制块 TCB 和一块任务栈,然后把这个任务加入就绪列表,交给调度器运行。

动态创建任务时,TCB 和任务栈都由 FreeRTOS 从它管理的堆中申请。对使用者来说,这种方式最简单,也最常见;只要堆空间足够,调用 xTaskCreate() 就可以完成任务创建。

一、动态创建任务 API

使用动态创建任务,需要在 FreeRTOSConfig.h 中打开:

#define configSUPPORT_DYNAMIC_ALLOCATION    1

动态创建任务函数:

BaseType_t xTaskCreate(
   TaskFunction_t pxTaskCode,
   const char * const pcName,
   const configSTACK_DEPTH_TYPE usStackDepth,
   void * const pvParameters,
   UBaseType_t uxPriority,
   TaskHandle_t * const pxCreatedTask
);

参数重点如下:

参数作用
pxTaskCode任务入口函数
pcName任务名称,方便调试
usStackDepth任务栈大小,单位是字,不是字节
pvParameters传给任务入口函数的参数
uxPriority任务优先级,数值越大优先级越高
pxCreatedTask任务句柄,用于后续删除、挂起、恢复任务

返回值:

返回值含义
pdPASS创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY创建失败,通常是堆内存不足

最小调用形式如下:

xTaskCreate(task1, "task1", 128, NULL, 2, &task1_handle);

任务创建成功后,会立即进入就绪态。至于什么时候真正运行,要看当前调度器状态以及任务优先级。

二、动态创建的使用流程

动态创建任务的使用过程可以压缩成三步:

  1. 打开 configSUPPORT_DYNAMIC_ALLOCATION
  2. 编写任务入口函数。
  3. 调用 xTaskCreate() 创建任务。

任务入口函数一般写成死循环:

void task1(void *pvParameters)
{
   while (1)
  {
       /* 任务逻辑 */
       vTaskDelay(pdMS_TO_TICKS(500));
  }
}

如果任务函数执行完就直接返回,这在 FreeRTOS 中通常不是一个好习惯。任务要结束时,应主动调用 vTaskDelete(NULL) 删除自己。

三、任务删除 API

使用任务删除函数,需要打开:

#define INCLUDE_vTaskDelete    1

函数原型:

void vTaskDelete(TaskHandle_t xTaskToDelete);

参数含义很直接:

参数含义
具体任务句柄删除指定任务
NULL删除当前正在运行的任务

示例:

vTaskDelete(task1_handle);  /* 删除 task1 */
vTaskDelete(NULL);          /* 删除当前任务 */

任务被删除后,会从就绪、阻塞、挂起、事件等列表中移除。若删除的是当前任务,FreeRTOS 会先把它放入等待删除列表,之后由空闲任务负责回收系统分配的 TCB 和任务栈。

这里有一个重要边界:空闲任务只负责释放 FreeRTOS 为任务分配的内存。如果任务运行过程中自己申请了内存、打开了外设资源或持有互斥量,这些资源必须在删除任务前由用户主动处理。

四、动态创建内部做了什么

从内核角度看,xTaskCreate() 大致做了这些事:

  1. 从 FreeRTOS 堆中申请任务栈。
  2. 从 FreeRTOS 堆中申请任务控制块 TCB。
  3. 初始化 TCB 中的任务名、优先级、栈地址等成员。
  4. 初始化任务栈,为后续上下文切换准备寄存器现场。
  5. 把任务加入对应优先级的就绪列表。
  6. 如果调度器已经运行,并且新任务优先级更高,则触发一次任务切换。

也就是说,xTaskCreate() 不只是“注册一个函数”,而是把一个任务运行所需的调度信息、栈空间和上下文环境都准备好。

五、TCB 是什么

TCB,全称 Task Control Block,即任务控制块。每个任务都有一个 TCB,可以理解为任务在内核里的身份档案。

常见关键成员包括:

成员作用
pxTopOfStack当前任务栈顶,任务切换时保存和恢复上下文
xStateListItem任务状态列表项,用于挂到就绪、阻塞、挂起等列表
xEventListItem事件列表项,用于事件等待机制
uxPriority任务优先级
pxStack任务栈起始地址
pcTaskName任务名称

任务调度时,FreeRTOS 不是直接“调度函数”,而是通过 TCB 找到任务栈、优先级、状态列表节点等信息,再完成上下文切换。

六、实验设计思路

本实验使用野火指南者开发板,主控芯片为 STM32F103VET6,平台使用STM32CubeIDE,故不是FreeRTOS 原生接口,而是由CMSIS-RTOS v2封装的FreeRTOS API 

实验说明:板载 LED 是一个 RGB 灯,不适合用多个独立 LED 分别表示多个任务,因此这里用不同颜色来区分任务现象:不同任务按照不同周期控制 RGB 灯显示不同颜色。

这个实验只围绕两个问题展开:

  1. 观察任务是如何被动态创建出来的。
  2. 观察指定任务被删除后,它对应的现象是否停止。

任务分工如下:

任务作用
start_task周期打印串口信息,作为系统仍在运行的心跳
task1每隔 1s 控制 RGB LED 显示蓝色
task2每隔 1.2s 控制 RGB LED 显示绿色,作为被删除的目标任务

按键触发前,start_task 会持续打印串口信息,说明调度器仍在正常运行;task1task2 也会按照各自周期运行。由于两个任务的周期分别是 1s1.2s,蓝色和绿色的切换节奏会不断错开,过一段时间后又重新接近重合。

按键触发后,中断服务函数不直接删除任务,而是只设置一个 volatile 标志位。随后在任务上下文中检测这个标志位,并删除 task2。删除成功后,绿色不再出现,RGB LED 只保留 task1 对应的蓝色变化;同时串口心跳仍然继续打印,说明删除的是指定任务,而不是整个系统停止运行。

这个实验的观察重点不是 RGB 灯本身,而是通过 RGB 灯和串口现象确认两件事:任务删除后,对应任务的行为会停止;其他未被删除的任务仍然可以继续被调度。

GitHub:Hui404/FreeRTOS_1

七、常见注意点

  1. usStackDepth 的单位是字,不是字节。
  2. 动态创建可能失败,要检查 xTaskCreate() 的返回值。
  3. 删除任务后,任务句柄变量不会自动变成 NULL,建议手动清空。
  4. 被删除任务自己申请的资源,必须在删除前释放。
  5. 动态创建依赖 FreeRTOS 堆,需要合理配置 configTOTAL_HEAP_SIZE
  6. 如果创建多个任务时不希望中途被调度打断,可以用短临界区保护创建过程。

八、小结

动态创建任务适合大多数普通应用场景。它的优势是简单灵活,内存分配由 FreeRTOS 负责;代价是创建过程依赖堆空间,存在申请失败的可能。

记住三个点就够了:

  1. xTaskCreate() 动态创建任务,成功后任务进入就绪态。
  2. vTaskDelete() 删除任务,传入 NULL 表示删除自身。
  3. FreeRTOS 只回收它分配的任务内存,用户自己申请的资源要自己释放。
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇