【详细】FreeRTOS任务的内部机制

一.结构体

volatile StackType_t * pxTopOfStack:保存当前任务堆栈的栈顶指针。

ListItem_t xStateListItem:用于将任务挂接到各种调度状态链表中。

ListItem_t xEventListItem:当任务因等待某个同步/通信对象(队列、信号量、互斥量、事件组等)而被阻塞时,内核会通过 xEventListItem 把任务挂接到该对象的 等待事件列表上。

UBaseType_t uxPriority:任务的调度优先级,0 为最低优先级。

StackType_t * pxStack:指向任务堆栈的起始地址(栈底)。

char pcTaskName[ configMAX_TASK_NAME_LEN ]:任务的描述性名称,仅用于调试(如 vTaskList() 输出),不参与调度逻辑。

二.创建

TaskFunction_t pxTaskCode:函数指针
const char * const pcName:函数名称
const configSTACK_DEPTH_TYPE usStackDepth:栈的大小
void * const pvParameters:函数参数
UBaseType_t uxPriority:任务优先级
TaskHandle_t * const pxCreatedTask :传出来的任务句柄,指向的TCB结构体

这里要注意一点的是,为什么创建任务的参数和TCB结构体对不上?多了函数指针和函数参数呢?

当调度器首次切换到该任务时,会从栈中“恢复”这些寄存器值,于是 CPU 就会直接跳转到该函数,并以 R0 中的值作为参数开始执行。所以,函数指针和参数是在栈里,而不是在 TCB 的成员中。

另外,任务创建完成后,内核会把新任务的 xStateListItem 插入到对应优先级的就绪链表 pxReadyTasksLists[ uxPriority ] 中。如果此时没有更高优先级的任务,pxCurrentTCB 会指向这个新任务,这样调度器在下一轮调度时就会先执行它。

三.调度

优先级与状态

  • 优先级不同

    • 高优先级的任务,优先执行,可以抢占低优先级的任务

    • 高优先级的任务不停止,低优先级的任务永远无法执行

    • 同等优先级的任务,轮流执行:时间片轮转

  • 状态

    • 运行态:running

    • 就绪态:ready

    • 阻塞:blocked,等待某件事(时间、事件)

    • 暂停:suspend,休息去了

  • 怎么管理?

    • 怎么取出要运行的任务?

      • 找到最高优先级的运行态、就绪态任务,运行它

      • 如果大家平级,轮流执行:排队,链表前面的先运行,运行1个tick后乖乖地去链表尾部排队

调度方法

  • 谁进行调度?

    • TICK中断!

  • 可抢占:高优先级的任务先运行

  • 时间片轮转:同优先级的任务轮流执行

  • 空闲任务礼让:如果有同是优先级0的其他就绪任务,空闲任务主动放弃一次运行机会

四.重点

怎么调度切换任务呢?最重要的核心就是这个PendSVHandler。

1.保存当前任务上下文

2.选择下一个要执行的任务

3.恢复新任务上下文并返回

找出当前优先级最高的就绪任务,并将其 TCB 赋值给 pxCurrentTCB

2个版本:

#define taskSELECT_HIGHEST_PRIORITY_TASK() \ { \ UBaseType_t uxTopPriority = uxTopReadyPriority; \ /* 从最高优先级向下找第一个非空的就绪链表 */ \ while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \ { \ configASSERT( uxTopPriority ); \ --uxTopPriority; \ } \ /* 取出该链表中的下一个任务,实现时间片轮转 */ \ listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ uxTopReadyPriority = uxTopPriority; \ } /*---------------------------------------------------------------------------------------*/ #define taskSELECT_HIGHEST_PRIORITY_TASK() \ { \ UBaseType_t uxTopPriority; \ /* 1. 从位图中直接得到最高就绪优先级数值 */ \ portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ /* 2. 确保该优先级的就绪列表非空(调试断言) */ \ configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ /* 3. 从该优先级链表中取出下一个任务交给 pxCurrentTCB */ \ listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ }