任务控制块及任务控制块链表:
uCOS-II用来记录任务的堆栈指针,任务的当前状态,任务的优先级别等一些与任务管理有关的属性的表就叫做任务控制块(OS_TCB) 。
任务控制块负责把任务代码和任务堆栈进行关联,而使任务控制块,任务代码和任务堆栈成为一个整体,并且系统要通过这个任务控制块来感知和管理一个任务。因此,任务控制块就相当于一个任务的身份证,没有任务控制块的任务是不能被系统承认和管理的。
为了管理系统中多个任务,uCOS-II把系统所有任务的控制块链接为两条链表,并通过这两条链表管理各任务控制块,进而再通过任务控制块来对任务进行相关的操作。
任务控制块的结构:
任务控制块是一个结构类型数据。当用户应用程序调用OSTaskCreate()函数创建一个用户任务时,这个函数就对任务控制块中的所有成员赋予与该任务相关的数据,并驻留在RAM中。
任务控制块结构的定义如下:
typedef struct os_tcb{
OS_STK *OSTCBStkPtr; //指向任务堆栈栈顶的指针
#if OS_TASK_CREATE_EXT_EN
void *OSTCBExtPtr; //指向任务控制块扩展的指针
OS_STK *OSTCBStkBottom; //指向任务堆栈栈底的指针
INT32U OSTCBStkSize; //任务堆栈的长度
INT16U OSTCBOpt; //创建任务时的选择项
INT16U OSTCBId; //目前该域未被使用
#endif
struct os_tcb *OSTCBNext; //指向后一个任务控制块的指针
struct os_tcb *OSTCBPrev; //指向前一个任务控制块的指针
#if (OS_Q_EN && (OS_MAX_OS >=2 )) || OS_MBOX_EN || OS_SEM_EN
OS_EVENT *OSTCBEventPtr; //指向事件控制块的指针
#endif
#if (OS_Q_EN && (OS_MAX_OS >=2 )) || OS_MBOX_EN
void *OSTCBMsg; //指向传递给任务消息的指针
#endif
INT16U OSTCBDly; //任务等待的时报(节拍数)
INT8U OSTCBStat; //任务的当前状态标志
INT8U OSTCBPrio; //任务的优先级别
INT8U OSTCBX; //用于快速访问就绪表的数据
INT8U OSTCBY; //用于快速访问就绪表的数据
INT8U OSTCBBitX; //用于快速访问就绪表的数据
INT8U OSTCBBitY; //用于快速访问就绪表的数据
#if OS_TASK_DEL_EN
BOOLEAN OSTCBDelReq; //请求删除任务时用到的标志
#endif
}OS_TCB;
其中成员OSTCBStat用来存放任务的当前状态,该成员变量可能的值:
值 说明
OS_STAT_RDY 表示任务处于就绪状态
OS_STAT_SEM 表示任务处于等待信号量状态
OS_STAT_MBOX 表示任务处于等待消息邮箱状态
OS_STAT_Q 表示任务处于等待消息队列状态
OS_STAT_SUSPEND 表示任务处于被挂起状态
OS_STAT_MUTEX 表示任务处于等待互斥型信号量状态
任务控制块链表:
uCOS-II用两条链表来管理任务控制块。一条是空任务块链表(其中所有任务控制块还没有分配给任务);另一条是任务块链表(其中所有任务控制块已经分配给任务)。空任务块链表是在应用程序调用函数OSInit()对uCOS-II系统进行初始化时建立的;而任务块链表则是在调用函数OSTaskCreate()创建任务时建立的,建立任务控制块链表的具体做法是,从空任务控制块链表摘取一个空任务控制块,然后填充上任务属性后,再形成新的链表。
系统在调用函数OSInit()对uCOS-II系统进行初始化时,先在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],这样每个数组元素就是一个任务控制块,然后把这些控制块链接成如下图所示的链表。由于链表中的这些控制块还没有与具体任务相关联,因此这个链表叫做空任务链表。
uCOS-II初始化时创建一个空的任务控制块链表
从上图可以看到,uCOS-II初始化时建立的空任务链表的元素一共是OS_MAX_TASKS+OS_N_SYS_TASKS个。其中定义在文件OS_CFG.H中的常数OS_MAX_TASKS指明了用户任务的最大数目。而定义在文件UCOS_H.H中的常数OS_N_SYS_TASKS指明了系统任务的数目(在上图中,其值为2;一个空闲任务,一个统计任务)
每当应用程序调用系统函数OSTaskCreate()或OSTaskCreateExt()创建一个任务时,系统就会将空任务控制块链表头指针OSTCBFreeList指向的任务控制块分配给该任务。在给任务控制块中的各成员赋值后,就按任务控制块链表的头指针OSTCBList将其加入到任务控制块链表中。
下图是在上图的空任务控制块链表基础上,应用程序创建了两个用户任务并使用了两个系统任务(空闲任务和统计任务)的情况时,空任务块链表和任务块链表的结构示意图(图中阴影区域为任务块链表)。
uCOS-II任务控制块链表和OSTCBPrioTbl[]数组及变量OSTCBCur
为了加快对任务控制块的访问速度,除了任务控制块链表被创建为双向链表之外,uCOS-II在uCOS_II.H文件中还定义了一个数据类型为OS_TCB* 的数组OSTCBTbl[],专门用来以任务的优先级别为顺序在各个数组元素里存放指向各个任务控制块的指针。这种系统在访问一个任务的任务控制块时,就可以不必遍历任务控制块链表了。数组OSTCBTbl[]与任务控制块链表中任务控制块之间的关系如上图所示。
另外,为了uCOS-II能随时访问正在运行任务的任务控制块,uCOS-II还定义了一个OS_TCB* 类型的变量OSTCBCur ,专门存放当前正在运行的任务的任务控制块指针。上图是在假设任务优先级别为3的任务正在运行时指针变量OSTCBCur的指向。
uCOS-II 允许用函数OSTaskDel()删除一个任务。删除一个任务,实质上就是把该任务的任务从任务控制块链表中删除,并把它归还给空任务控制块链表。这样uCOS-II对这个没有任务控制块的任务就不再理会了,因为与这个任务对应的任务控制块已经被"吊销"了。由此可见,任务的任务控制块就如同人的身份证一样重要。
任务控制块的初始化:
给用户任务分配任务控制块及对其进行初始化也是操作系统的职责。当应用程序调用函数OSTaskCreate()创建一个任务时,这个函数会调用系统函数OS_TCBInit()来为任务控制块进行初始化。这个函数首先为被创建任务从空任务控制块链表获取一个任务控制块;然后用任务的属性对任务控制块各个成员进行赋值;最后再把这个任务控制块链入到任务控制块链表的头部。
初始化任务控制块函数OS_TCBInit()的原型如下:
INT8U OS_TCBInit(
INT8U prio, //任务的优先级别,保存在OSTCBPrio中
OS_STK *ptos, //任务堆栈栈顶指针,保存在OSTCBStkPtr中
OS_STK *pbos, //任务堆栈栈底指针,保存在OSTCBStkBottom中
INT16U id, //任务的标识符,保存在OSTCBId中
INT16U stk_size, //任务堆栈的长度,保存在OSTCBStkSize中
void *pext, //任务控制块的扩展指针,保存在OSTCBExtPtr中
INT16U opt //任务控制块的选择项,保存在OSTCBOpt中
);