一、就绪列表的结构
就绪列表是一个数组,数组元素的数量和最大支持的优先级数量一致。若最大任务优先级设置为 56 ,就绪列表会是一个包含 56 个元素的数组,同时会存在 56 个就绪链表。数组里每个元素存放的是对应优先级下的任务控制块的指针。
#define configMAX_PRIORITIES 56
// 就绪列表数组,每个元素是一个任务控制块指针
TaskHandle_t *pxReadyTasksLists[ configMAX_PRIORITIES ];
任务控制块里与链表相关的成员,可用于把任务连接到链表之中。
ListItem_t xStateListItem;
ListItem_t xEventListItem;
// 将任务的 xStateListItem 插入到就绪任务列表中
vListInsert(&readyTaskList, &task1.xStateListItem);
xStateListItem:
作用是将任务连接到状态列表,借助这个列表项能够明确任务所处的状态,例如就绪、阻塞、挂起等。不同状态的任务会被组织成不同的链表,而 xStateListItem
可让任务成为这些链表中的一个节点。
xEventListItem
:作用是将任务连接到事件列表。当任务等待某个事件(像信号量、消息队列等)时,就会通过 xEventListItem
被添加到对应的事件等待链表中。
二、任务调用逻辑
在 FreeRTOS 中任务的执行主要取决于 pxCurrentTCB
指向哪个任务。
1. 确定最高优先级的就绪任务链表
在调度器进行任务选择时,首先会找出所有就绪任务中的最高优先级,这个最高优先级存储在 uxTopReadyPriority
变量中。然后通过 pxReadyTasksLists[uxTopReadyPriority]
找到对应最高优先级的就绪任务链表。
2. 同等优先级任务的轮换
- 时间片检查:系统时钟节拍中断会周期性触发,在中断处理函数中会检查当前正在执行的任务的时间片是否用完。如果时间片用完,就会触发任务切换。
- 选择下一个任务:每个任务控制块(TCB)中包含一个
xStateListItem
链表节点,通过该节点可以将任务连接到就绪链表中。当需要进行任务切换时,调度器会从当前任务所在的链表中找到下一个任务。通常是从当前任务的xStateListItem
开始,通过其pxNext
指针找到链表中的下一个任务节点。 - 更新
pxCurrentTCB
:找到下一个要执行的任务后,将pxCurrentTCB
赋值为该任务的 TCB 指针。pxCurrentTCB
是一个指向当前正在执行任务的 TCB 的指针,更新它意味着将控制权切换到新的任务
TCB_t *pxNextTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxNextListItem );
3. 任务执行
- 保存当前任务的上下文:把当前任务的寄存器值(如通用寄存器、程序计数器等)保存到该任务的栈中,并更新当前任务的栈指针。
- 恢复新任务的上下文:从
pxCurrentTCB
指向的新任务的栈中恢复寄存器值,同时更新处理器的栈指针为新任务的栈指针。 - 跳转到新任务的代码执行:将程序计数器设置为新任务的入口地址,新任务开始执行。
三、链表
1.List_t
(链表)
用于管理链表,为任务调度、事件管理等功能提供了基础。管理了就绪任务列表,阻塞任务列表,事件等待列表。
//List_t 结构体及成员作用
typedef struct xLIST
{
UBaseType_t uxNumberOfItems; // 链表中节点的数量
ListItem_t *pxIndex; // 当前索引指向的节点
MiniListItem_t xListEnd; // 链表的结束标记节点
} List_t;
2. ListItem_t
(链表项)
链表项是构成链表节点的基础,主要用于将任务控制块(TCB)或者其他需要管理的对象连接到链表中,以实现对任务、事件等的高效管理和调度。
//ListItem_t 结构体及成员作用
typedef struct xLIST_ITEM
{
// 用于将节点插入到链表中的指针
struct xLIST_ITEM * pxNext;
struct xLIST_ITEM * pxPrevious;
// 指向包含该节点的列表的指针
List_t * pxContainer;
// 用于排序或比较的数值,通常与任务的优先级等相关
TickType_t xItemValue;
} ListItem_t;
vListInsert 是 FreeRTOS 提供的一个函数,用于将一个链表项(ListItem_t)插入到指定的链表(List_t)中。通过调用 vListInsert 函数,将任务的 xStateListItem 插入到对应优先级的就绪任务链表中,从而实现将任务添加到相应链表的目的。
// 定义就绪任务链表数组,每个数组元素代表着一个链表
List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
// 初始化就绪任务链表,轮询初始化所有优先级链表
void vInitialiseReadyTaskLists( void )
{
UBaseType_t uxPriority;
for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
{
vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );//初始化链表
}
}
// 将任务添加到就绪任务链表
void vAddTaskToReadyList( TaskHandle_t pxTask )
{
UBaseType_t uxPriority = pxTask->uxPriority;
vListInsert( &( pxReadyTasksLists[ uxPriority ] ), &( pxTask->xStateListItem ) );
}