1.UC/OS-II中的任务有两种:用户任务和系统任务。用户任务是为解决问题而编写的;系统任务是为应用程序来提供某种服务的。为了管理方便,UC/OS-II把每一个任务都作为一个节点,然后把他们连接成一个任务链表,目前,UC/OS-II可将64个任务进行管理。
2.1.1任务的状态
嵌入式系统中只有一个CPU,所以在一个具体时刻只有一个任务占用CPU;根据任务是否占用CPU,以及是否处于被中断,等待等情况,任务在UC/OS-II中可能处于下表中状态之一:
1.睡眠状态:任务只是以代码的形式驻留在程序空间(ROM或ROM),还没有交给操作系统管理时的情况叫做睡眠状态,简单地说,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态。
2.就绪状态:如果系统为任务配备了任务控制块并且在任务就绪表中进行了就绪登记,则任务就具备了运行的充分条件。
3.运行状态:处于就绪状态的任务如果经调度器判断获得了CPU的使用权,则任务就进入了运行状态,任何时刻只能有一个任务处于运行状态,就绪的任务只有当所以优先级高于本任务的任务都转为等待状态时,才能进入运行状态。
4.等待状态:正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给其他任务而使任务进入等待状态。
5.中断服务状态:一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序。
任务在不同状态下的转换如下:
2.1.2 用户任务代码的一般结构
1.用户任务代码的一般结构
任务的执行代码通常是一个无限循环结构,并且在这个循环中可以响应中断,这种结构也叫做超循环结构。
一个UC/OS-II任务的代码就是一个C语言函数,为了可以传递各种不同类型的数据甚至是函数,任务的参数是一个void类型的指针。
为了有效地对中断进行控制,在任务的代码里可使用UC/OS-II定义的宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来控制任务何时响应中断,何时屏蔽中断,在运行这两个宏之间的代码时是不会响应中断的,这种受保护的代码段叫做临界段。在具体应用中,可使用这对宏设置多个临界段。
因此,UC/OS-II任务的代码结构是一个带有临界段的无限循环。
2.用户应用程序的一般结构
从程序代码上来看,用户任务是一个c语言函数,但是这个函数不是一般的c语言函数,他是一个任务(线程),因此,他不是被主函数调用的,主函数main()只负责创建和启用它们,而由操作系统来负责调度运行它们。
2.1.3 系统任务
UC/OS-II预定义了两个为应用程序服务的系统任务:空闲任务和统计任务。其中,空闲任务是每个应用程序必须使用的,而统计任务则是应用程序可以根据实际需要来选择使用的。
1.空闲任务
在多任务系统运行时,系统经常会在某个时间内无用户任务可执行而处于所谓的空闲状态,为了使CPU在没有用户任务可执行是有事可做,UC/OS-II提提供了一个叫做空闲任务OSTaskIdle()的系统任务。
空闲任务时,几乎不会做任何事情,只是对系统定义的一个空闲任务运行次数计数器OSdleCtr进行加1操作,也可以在空闲任务中编写一些做用户工作的代码。
UC/OS-II规定一个用户应用程序必须使用这个空闲任务,而且这个任务是不能用软件来删除的。
2.统计任务
UC/OS-II提供的另一个系统任务是统计任务OSTaskStat(),这个统计任务每秒计算一次CPU在单位时间内被使用的时间,并把计算结果以百分比的形式存放在变量OSCPUUsage中,以便应用程序通过访问它来了解CPU的利用率,所以该系统任务OSTaskStat()叫做统计任务。
若用户应用程序要使用这个统计任务,则必须把定义在系统文件OS_CFG.H中的系统配置常数OS_TASK_STAT_EN设置为1,并且必须在创建统计任务之前调用函数OSStatInit()对任务进行初始化。
2.1.4任务的优先权及优先级别
1.一般,一个应用程序任务数小于64,uc/OS-II把任务的优先权分为64个优先级别(0优先级最高)
2.在文件OS_CFG.H中通过给最低优先级别常数OS_LOWEST_PRIO赋值来说明应用程序中任务优先级别的数目。最低优先级赋给空闲任务,其次统计任务。用户任务使用的优先级是0,1,2,…OS_LOWEST_PRIO-2,共OS_LOWEST_PRIO-1个。
3.在调用系统函数OSTaskCreate()时,可通过修改第四个参数prio指定优先级。
2.2任务堆栈
2.2.1任务堆栈的创建
任务堆栈是任务的重要组成部分。
typedef unsigned intOS_STK;//该类型长度为16位
#define TASK_STK_SIZE512 //定义堆栈的长度(1024字节)?
OS_STKTaskStk[TASK_STK_SIZE];//定义一个数组来作为任务堆栈
INT8U OSTaskCreate(
void (*task)(void *pd),//指向任务的指针
void *pdata, //传递给任务的参数
OS_STK *ptos, //任务堆栈栈顶的指针
INT8Uprio //指定任务优先级别的参数
);
当调用函数OSTaskCreate()来创建一个任务时,把数组的指针传递给函数OSTaskCraete()中的堆栈栈顶参数ptos,就可以把该数组与任务关联起来而成为该任务的任务堆栈。
在使用OSTaskCreate()创建任务时,一定要注意所使用的处理器对堆栈增长方向的支持是向上的还是向下的。
向上增长型:堆栈增长方向由低地址向高地址增长;
向下增长型:堆栈增长方向由高地址向低地址增长;
在编写代码时,为了提高代码的可移植性,可利用OS_CFG.H文件中的常数OS_STK_GROWTH作为选择开关,使用户可通过定义该常数的值来选择相应的代码段。
#define MyTaskStkN 64
OS_STK MyTaskStk[MyTaskStkN];
void main(void)
{
…..
#if OS_STK_GROWTH == 1
OSTaskCreate(
MyTask,
&MyTaskAgu,
&MyTaskStk[MyTaskStkN - 1],
20
);
#else
OSTaskCreate(
MyTask,
&MyTaskAgu,
&MyTaskStk[0],
20
);
#endif
……
}
2.2.2任务堆栈的初始化
任务堆栈的初始化,由OSTaskStkInit()来完成
OS_STK *OSTaskStkInit(
void( *task)(void *pd),
void * pdato,
OS_STK *ptos,
INT16U opt
);
2.3任务控制块及任务控制块链表
任务控制块(OS_TCB):记录任务的堆栈指针,任务的当前状态,任务的优先级别等一些与任务管理有关的属性。
为了管理系统中的多个任务,UCOS-II把系统所有任务的控制块链接为两条链表并通过这两条链表管理各任务控制块,进而再通过任务控制块来对任务进行相关操作。
2.3.1 任务控制块的结构
任务控制块是一个结构类型数据。当用户应用程序调用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_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
OS_EVENT *OSTCBEventPtr; //指向事件控制块的指针
#endif
#if (OS_Q_EN &&(OS_MAX_QS >= 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 任务处于互斥型信号量状态
2.3.2 任务控制块链表
UCOS-II中用两条链表来管理任务控制块。
1.空任务块链表
因为此链表中这些控制块还没有分配给任务,所以叫做空任务链表。
大小为ucos-II.H中定义的OS_MAX_TASKS + OS_N_SYS_TASKS(即用户任务最大数+系统任务总数)个。其初始化由系统初始化函数OSInit()完成。其头指针为OSTCBFreeList,是一个OS_TCB类型的数组。即OS_TCB OSTCBTbl[OS_MAX_TASKS +OS_N_SYS_TASKS];
2.任务链表
每当应用程序调用系统函数OSTaskCreate()或OSTaskCreateExt()创建一个任务的时候,系统就会将空任务链表头指针OSTCBFreeList 指向的任务控制块分配给该任务。在给任务控制块中的成员赋值后,就将其加入OSTCBList所指向的任务控制链表中,于是,就出现了任务链表。
3.任务控制块访问优化:
为了加快对任务控制块的访问速度,任务链表为双向链表。同时,设置了一个OS_TCB*类型的指针数组OS_TCBTbl[]。其中以任务的优先级为顺序在各数组元素中存放指向该任务控制块的指针。
同样,为了能随时访问正在运行任务的任务控制块,还定义了一个OS_TCB*类型的OSTCBCur,专门用于存放当前正在运行的任务的任务控制块的指针。
4. 任务删除
OSTaskDel()删除任务的过程:把被删除的任务控制块从任务控制块链表中删除,并归还给空任务控制块链表,然后在任务就绪表中将该任务的就绪状态置0。(注:可以通过该函数来删除任务自身或者除了空闲任务之外的其他任务)。
2.3.3任务控制块的初始化
初始化任务控制任务块函数
INT8U OSTCBInit(
INT8U prio, //任务的优先级别,保存在OSTCBPrio 中
OS_STK *ptos, //任务堆栈栈顶指针,保存在 OSTCBStkPtr中
OS_STK *pbos, //任务堆栈栈底的指针,保存在OSTCBStkBottom中
INT16U id, //任务的标识符,保存在OSTCBId找那个
INT16U stk_size, //任务堆栈的长度,保存在OSTCBStkSize中
void *pext, //任务控制块的拓展指针,保存在OSTCBExtPtr中
INT16U opt, //任务控制块的选择项,保存在OSTCBOpt中
}