[ 附件影像 RECORD ]
FreeRTOS学习笔记 · 第七讲:2.4freertos任务管理工具函数

FreeRTOS学习笔记 · 第七讲:2.4freertos任务管理工具函数

[ SCAN_URL ]
[ 归档时间 ]:2026-03-23 17:36 [ 课题责任人 ]:文止墨 [ 档案分类 ]:嵌入式, 文章, 编程
*1774287415*

获取任务句柄

osThreadNew

创建新任务。

当使用这个函数创建新任务时,该函数会返回一个osThreadId_t类型的值。

实际上该函数是调用xTaskCreatexTaskCreateStatic来创建任务的,前者会返回BaseType_t类型并通过指针返回句柄,后者会返回TaskHandle_t类型。

不论是那种方式,最终任务句柄都会被osThreadNew返回。

xTaskGetCurrentTaskHandle

获取当前任务句柄。

使用场景:

  • 创建任务是未保存该任务句柄;
  • 多个任务都会调用某一函数,函数内部需要知道到底是哪个任务调用的;
  • 多任务通信,需要告诉对方自己的句柄。

xTaskGetIdleTaskHandle

获取空闲任务句柄。

可以通过统计空闲任务运行时间来得到实际 CPU 利用率。

最开始这个函数是默认不编译的,并且不能在 cubemx 里设置。

如果你需要这个任务,请在FreeRTOSConfig.h里自己添加宏定义:

xTaskGetHandle

工具任务名称获取任务句柄,同样默认不编译,但可以通过 cubemx 启用。

通过任务名称字符串查询任务句柄,当两个任务具有相同的名称时,返回结果不确定。

注意,该函数查询所需的时间可能会很长,不建议频繁使用。

任务优先级操作

uxTaskPriorityGet

通过句柄返回对应任务的优先级,UBaseType_t类型。

当传递 null 时,返回当前正在运行的任务的优先级。

除此之外,它还有用于中断函数里的版本uxTaskPriorityGetFromISR,传入 null 时,会返回被当前中断打断的任务优先级。

为什么只是读个优先级,还要分两个函数?

这主要是为了系统的绝对稳定兼容不同架构的处理机制

  • 临界区(Critical Sections)的实现不同: 为了防止在读取优先级的瞬间,发生任务调度或者高优先级中断嵌套导致数据错乱,FreeRTOS 在内部可能需要短暂地进入临界区。
  • uxTaskPriorityGet() 使用的是 taskENTER_CRITICAL()。它假设你目前处于任务态(Thread Mode),直接去操作 CPU 的中断屏蔽寄存器。
  • uxTaskPriorityGetFromISR() 使用的是 portSET_INTERRUPT_MASK_FROM_ISR()。由于你已经处于中断态(Handler Mode),甚至可能处于中断嵌套中,这个函数会非常小心地保存当前的中断屏蔽状态,读取完数据后,再原封不动地恢复回去。

如果你在中断里调用了普通的 uxTaskPriorityGet,它的临界区操作可能会错误地完全打开中断,破坏中断嵌套的优先级逻辑,直接导致 ARM 处理器触发 HardFault(硬错误)死机。

  • MPU(内存保护单元)机制差异: 在一些对安全性要求极高的工程中会开启 MPU。任务通常运行在“非特权级”,而中断永远运行在“特权级”。由于运行权限不同,底层去访问包含优先级的 TCB 内存块时的硬件指令和权限校验流程是完全不同的。

vTaskPrioritySet

通过句柄设置任务的优先级。

句柄传递为 null 时,设置当前任务的优先级。

当传入 CMSIS2 里定义的枚举型时,需要强转为UBaseType_t

任务信息操作

vTaskGetInfo

获取单个任务信息。

用法

C
void vTaskGetInfo( TaskHandle_t xTask,
                   TaskStatus_t *pxTaskStatus,
                   BaseType_t xGetFreeStackSpace,
                   eTaskState eState );
  • xTask (目标任务句柄): 想查询的那个任务的句柄。如果传入 NULL,查询当前正在运行的任务(。
  • pxTaskStatus (状态结构体指针): 需要提前定义一个 TaskStatus_t 类型的变量,把它的地址传进去。函数会将任务信息返回到该地址。
  • xGetFreeStackSpace (是否计算剩余栈空间):
  • pdTRUE:系统会去计算该任务的历史最小剩余栈空间(High Water Mark)。注意:计算栈空间需要遍历未使用的栈内存,比较耗时。
  • pdFALSE:跳过计算,结构体里的栈剩余字段将被设为 0。如果为了追求极速执行,建议传这个。
  • eState (任务当前状态):
  • 如果你已经确切知道这个任务目前的状态(如 eBlocked, eSuspended 等),可以直接传进去,这样能省去系统自己去查找状态的时间。
  • 如果你不知道,直接传 eInvalid,FreeRTOS 的调度器会自动去各个状态的链表里寻找到它的真实状态。

返回值

函数执行完毕后,你可以从传入的结构体中读取到以下数据:

  • xHandle: 任务句柄。
  • pcTaskName: 任务名称(字符串指针)。
  • xTaskNumber: 任务的唯一编号(创建时的序号)。
  • eCurrentState: 当前状态(如就绪、阻塞、挂起等枚举值)。
  • uxCurrentPriority: 当前的真实优先级(如果任务使用了互斥锁并发生了优先级继承,这个值可能会高于它的初始优先级)。
  • uxBasePriority: 任务创建时的初始基准优先级。
  • ulRunTimeCounter: 任务总共占用 CPU 的运行时间(前提是开启了运行时间统计宏)。
  • usStackHighWaterMark: 栈区历史最低剩余容量(注意:单位是“字 (Word)”,在 32 位系统上 1 个字 = 4 字节)。

pcTaskGetName

通过句柄获取任务名称字符串。

uxTaskGetStackHighWaterMark

通过句柄获取栈区历史最低剩余容量(栈高水位值)。

除此之外,还有uxTaskGetStackHighWaterMark2,用于更好的兼容不同位的单片机。在 32 位控制器上,两者表现完全一致。

eTaskGetState

通过句柄获取任务状态,返回eTaskState型。

内核信息统计

uxTaskGetNumberOfTasks

返回当前任务总数,包括就绪、阻塞、运行、挂起和虽然删除但仍未释放的任务。

vTaskList

以字符串表格的形式展示所有任务情况。默认不编译。

需要传入一个足够大的字符串数组,且 freertos 不会检查数组大小。

该函数使用 sprintf 函数,会使编译后的固件大小明显增大,最好仅在调试中使用。

uxTaskGetSystemState

获取系统内所有任务的状态,并返回到参数指定的数组里。函数返回值表示成功填入数组的数据的数量

C
UBaseType_t uxTaskGetSystemState( 
    TaskStatus_t * const pxTaskStatusArray, 
    const UBaseType_t uxArraySize, 
    uint32_t * const pulTotalRunTime 
);

参数说明

pxTaskStatusArray (任务状态数组指针)

  • 作用:这是一个输出参数。用户需要事先在内存中准备好一个 TaskStatus_t 类型的数组,然后把这个数组的首地址传给它。FreeRTOS 会把扫描到的所有任务的详细状态信息(如任务名字、当前状态、优先级、剩余堆栈等)逐一填入这个数组中。

uxArraySize (数组容量)

  • 作用:这是一个输入参数。它告诉 FreeRTOS 刚才传入的那个数组最多能装下多少个任务的信息。
  • 避坑要点:通常会先调用 uxTaskGetNumberOfTasks() 获取当前系统真实存在的任务总数,并据此来设定数组大小。如果传入的 uxArraySize 小于当前系统中实际存在的任务总数,函数为了防止内存越界,会直接放弃操作,返回 0,并且不会向数组中写入任何有效数据。

pulTotalRunTime (系统总运行时间指针)

  • 作用:这是一个输出参数(可选)。它用来接收自系统调度器启动以来的“总运行时间”。
  • 灵活使用:这个总时间值通常配合各个任务状态结构体里的 ulRunTimeCounter(该任务独立的运行时间)一起使用,通过简单的除法就可以计算出每个任务占据 CPU 时间的百分比。如果你不需要计算 CPU 占用率,或者没有开启运行时间统计宏,可以直接给这个参数传入 NULL,FreeRTOS 就会安全地忽略它。

vTaskGetRunTimeStats

以文字表格的形式返回每个任务的运行时间。

该函数会在调用时禁用所有中断,因此尽量不要在正常运行时调用它。

xTaskGetSchedulerState

获取调度器状态的函数。

返回结果

  • taskSCHEDULER_NOT_STARTED (未启动)
  • 含义:调度器还没开始工作。通常发生在你刚上电,在 main() 函数里进行了一堆外设初始化(比如初始化 GPIO、I2C),创建了几个任务,但还没有调用 vTaskStartScheduler()(或者 CMSIS-RTOS 封装的 osKernelStart())之前。
  • taskSCHEDULER_RUNNING (正在运行)
  • 含义:调度器正在正常工作,各个任务正在有条不紊地按照优先级和时间片(如果你开启了的话)交替运行。
  • taskSCHEDULER_SUSPENDED (已挂起)
  • 含义:调度器原本在运行,但是被某段代码通过调用 vTaskSuspendAll() 强行“冻结”了。在这个状态下,哪怕有更高优先级的任务就绪了,也不会发生上下文切换,直到调用 xTaskResumeAll() 为止。(注:挂起调度器不会关闭硬件中断,但中断里不能要求切换任务)。

[ 发起通讯连接 / INITIATE COMM-LINK ]

[SYS]: 您的回传节点(邮箱)将被严格保密。带有 * 的字段为必填项。


> 终止读取并返回主控制台 <