【详细】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 ] ) ); \ }