第一节 前言
FreeRTOS 在学习完FreeRTOS的创建与删除后,下一步需要学习在项目的整个运行过程中,任务处于哪一种状态。FreeRTOS 中的任务永远处于下面四个状态,运行态、就绪态、阻塞态、挂起态。任务在四个状态中转换,只有处于运行态的任务才是当前进程中正在运行的任务。下面是对四种状态的学习。
第二节 任务的状态
个人觉得,在开始学习阶段不需要花费过多的时间学习其中状态的转换过程,只需要理解处于每个状态的任务在整个项目运行过程中的参与情况。
a.运行态
当一个任务正在运行时, 该任务则处于运行态, 处于运行态的任务就是当前正在使用处理器的任务。 处于运行态的任务一定是就绪链表中优先级最高的任务(所以运行态的任务本质是从就绪链表中取出的优先级最高的任务)。对于单核处理器来说,不管在任何时刻FreeRTOS同一时刻只能有一个任务在运行态。
b.就绪态
处于就绪态的任务是那些已经准备就绪(即没有被阻塞或者挂起), 已具备执行的能力可以随时准备运行的任务,等待调度器进行调度,新创建的任务会直接初始化为就绪态。
c.阻塞态
一个任务进入阻塞态有两种情况,一个任务当前正在等待某个外部事件的话就说它处于阻塞态(任务需要等待队列、信号量、事件组、通知或互斥信号量),或者任务因为某个超时时间而处于阻塞态(某个任务调用了函数 vTaskDelay())。进入阻塞态的任务从就绪链表中移除,任务被添加至延时链表/等待事件上的链表。
d.挂起态
任务进入挂起态后不能被调度器调用进入运行态。挂起的任务会被挂入悬起链表中,表示任务已暂停,让一个任务进入挂起状态的唯一办法就是调用vTaskSuspend()函数,而 把 一 个 挂 起 状态 的FreeRTOS任 务 恢复的 唯 一 途 径 就 是 调用vTaskResume() 或 vTaskResumeFromISR()函数,在挂起态的任务永远不可能参与调度。
第三节 列表(链表)
FreeRTOS中调度大量使用了列表(list)和列表项(list item)数据结构,任务的四种状态就是使用列表来表示的。
1. 列表项
下面是列表项结构体,只介绍使用列表项中所用到的元素,可以把列表项直接当作链表来看待。列表项服务与任务,每一个任务都会有对应的自身的列表项。
struct xLIST_ITEM
{
configLIST_VOLATILE TickType_t xItemValue; /*列表项值*/
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*指向列表中下一个列表项*/
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*指向列表中上一个列表项*/
void * pvOwner; /*指向拥有该列表项对应任务的TCB*/
void * configLIST_VOLATILE pvContainer; /*指向包含该列表项的列表 */
};
typedef struct xLIST_ITEM ListItem_t;
在使用xTaskCreate创建任务时,会初始化一个任务控制块结构体。下面是对TCB的常用的元素的中会使用列表项。
typedef tskTCB TCB_t;
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //位于TCB第一项的栈顶指针,便于对任务控制块TCB进行操作
ListItem_t xStateListItem; //任务状态列表项,对于任务处于四种状态之一的挂接
ListItem_t xEventListItem; //事件列表项
UBaseType_t uxPriority; //任务优先级,数值越大优先级越高
StackType_t *pxStack; //指向堆栈的起始位置
char pcTaskName[ configMAX_TASK_NAME_LEN ]; //任务名字
....... ....... //结构体中的内容后续再做分析
} tskTCB;
2. 列表项的初始化
a.初始化列表项的调用过程如下:
xTaskCreate() //创建任务
-> prvInitialiseNewTask() // 初始化任务,对TCB中的各字段初始化
- >vListInitialiseItem( &( pxNewTCB->xStateListItem ) ) //列表项初始化
->listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ) //设置列表项的pvOwner
b.将任务添加到就绪列表中
prvAddNewTaskToReadyList( pxNewTCB ) ->prvAddTaskToReadyList( pxNewTCB )
在task.c中已经定义了全局变量pxReadyTasksLists[]作为就绪列表
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
其他的状态列表在Study任务调度器时再进行分析。