μC/OS Ⅱ中任务的基本概念
任务管理机制是嵌入式操作系统的核心,保证了对于嵌入式操作系统重要的实时性。μC/OS Ⅱ是一款嵌入式实时多任务操作系统,多任务的实现是靠CPU在许多任务之间完成转换和调度,CPU轮番服务于一系列任务之中优先级最高的任务。
在μC/OS Ⅱ中,任务就是一个用户自己编写的一个函数。其基本结构如下:
void TaskName(void *pdata){
for( ; ; ){
/*可以被中断的用户代码*/
OS_ENTER_CRITICAL(); //进入临界区(关中断)
/*不可以被中断的用户代码*/
OS_EXIT_CRITICAL(); //退出临界区(开中断)
/*可以被中断的用户代码*/
}
}
任务执行的代码通常是一个无限循环的结构体(一些一次性执行完的任务除外)。为了向任务中传递不同类型的参数,所以定义了一个void类型的指针,这就是万能指针,指针pdata可以指向任意类型的数据。
μC/OS Ⅱ的任务共有五种状态,睡眠态、就绪态、运行状态、等待状态、中断状态。
睡眠态是指任务只以代码的形式存在于程序空间,也就是说这时任务没有被分配任务控制块或已经被剥夺任务控制块。
就绪态是指任务得到了任务控制块以及其它一切运行需要的充分条件,在等待CPU调度运行。
运行状态是指获得了CPU资源的任务的状态。
等待状态是指一个任务正在运行,在运行期间需要等待一个事件发生,或者任务本身需要延时一段时间,这时候任务进入等待状态,将CPU使用权交付给其他任务。
中断状态就是一个正在运行的任务,响应了中断申请就会中止运行而去执行中断服务程序。
μC/OS Ⅱ是一个实时抢占式的操作系统,CPU根据任务优先级的高低始终调度优先级最高的就绪任务来运行。μC/OS Ⅱ规定每个任务的优先级必须不同。μC/OS Ⅱ最多可以创建64个任务,所以任务的优先级是由0、1、2、....、63。系统在系统配置文件OS_CFG.H中定义了常量OS_LOWEST_PRIO用来表示系统允许的最低优先级,也就是说系统中的最多任务数是可以由用户自定义的最多为OS_LOWEST_PRIO+1,优先级为0~OS_LOWEST_PRIO。μC/OS Ⅱ中定义了两个系统任务,空闲任务和统计任务,优先级分别为OS_LOWEST_PRIO和OS_LOWEST_PRIO-1。
μC/OS Ⅱ中任务的相关数据结构
任务堆栈
μC/OS Ⅱ任务的堆栈用来保存任务运行需要的一些相关信息,例如指向任务的指针、程序状态字等CPU寄存器的值,也就是说任务堆栈中需要对任务运行所需要的生存环境进行保存。在任务创建的时候,任务创建函数需要调用OSTaskStkInit( )函数对任务堆栈进行初始化,对任务堆栈预置一些初始数据,这样,在任务获得CPU资源运行的时候,就可以将这些数据弹出到CPU的各个寄存器中。下面是OSTaskStkInit( )函数的原型:
OS_STK*OSTaskStkInit (void(*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
其中参数task是一个返回类型为void,带有一个万能指针参数的函数指针。pdata是向任务传递的参数,ptos是任务堆栈栈顶任务指针.opt是一个选项函数,如果在OSTaskCreate函数中调用堆栈初始化函数,则这个选项没有意义,可传0.如果创建任务函数用的是OSTaskCreateExt函数,这个参数有特定的意义,这个在后面讨论。
另外,根据CPU硬件设置的不同,堆栈的增长方向也不同(从高地址到低地址,或从低地址到高地址),要对OS_CFG.H文件中的宏OS_STK_GROWTH进行设置,这是移植时候要注意的。
为任务堆栈开辟空间的时候可以选择直接用OS_STK定义数组也可以用其定义一个指针变量,然后用malloc进行内存动态分配。
任务控制块
TCB(任务控制块)是μC/OSⅡ中用来记录任务的堆栈指针、任务的当前状态、任务优先级等一系列与任务管理有关的属性的一个数据结构。任务控制块可以说是一个任务的身份证,系统就是通过任务控制块来管理任务的。前面提到任务的状态时说过,一个任务如果没有被分配TCB,那么它就处于睡眠态,是不能被CPU感知和执行的。对于TCB的分配工作在任务创建函数OSTaskCreate()或OSTaskCreateExt()中进行。
在μC/OS Ⅱ中对于TCB的管理是通过任务控制块列表来完成的,任务控制块列表有两条,一条是空任务控制块列表,一条是已分配给任务的任务控制块列表。做法是系统在OSInit()对系统初始化时,在RAM中建立OS_TCB机构数组OSTCBTbl[],形成一个空白链表。在OS_CFG.H中有参数OS_MAX_TASKS(用户最大任务数),μC/OS-II.H中的常数OS_N_SYS_TASKS是指定系统任务数,所以这个空白链表的初始长度就是这两个常数的和。一般是2个:空闲任务和统计任务。每当系统调用任务创建函数创建任务时,就将空任务控制块链表表头指针OSTCBFreeList指向的任务控制块分配给该任务,然后给任务控制块中的各个值赋值,按照任务控制块链表表头指针将该TCB加入到任务控制块链表中。对于TCB初始化时通过函数OSTCBInit()来进行的。TCB的结构体定义及注释如下:
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /*任务堆栈栈顶*/
#if OS_TASK_CREATE_EXT_EN > 0ter to user definable data for TCB extension
//如果使用任务控制块扩展
void *OSTCBExtPtr; /*指向任务控制块扩展的指针*/
OS_STK *OSTCBStkBottom; /*指向任务堆栈栈底的指针*/
INT32U OSTCBStkSize; /*任务堆栈长度*/
INT16U OSTCBOpt; /* 创建任务时的选择项 */
INT16U OSTCBId; /* 目前该域未被使用*/
#endif
struct os_tcb *OSTCBNext; /*指向后一个TCB */
struct os_tcb *OSTCBPrev; /*指向前一个TCB */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
OS_EVENT *OSTCBEventPtr; /* 指向任务事件控制块的指针*/
#endif
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
void *OSTCBMsg; /*指向传递给任务消息的指针 */
#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
OS_FLAG_NODE *OSTCBFlagNode;
#endif
OS_FLAGS OSTCBFlagsRdy;
#endif
INT16U OSTCBDly; /* 任务延时的时间 */
INT8U OSTCBStat; /* 任务的当前状态标志*/
INT8U OSTCBPrio; /* 任务优先级*/
INT8U OSTCBX; /*用于快速访问就绪表的数据*/
INT8U OSTCBY; /*用于快速访问就绪表的数据 */
INT8U OSTCBBitX; /*用于快速访问就绪表的数据 */
INT8U OSTCBBitY; /*用于快速访问就绪表的数据 */
#if OS_TASK_DEL_EN > 0
BOOLEAN OSTCBDelReq; /* 删除任务请求标志*/
#endif
} OS_TCB;
※注※ 因为优先级最大范围为0~63,所以可以用7位2进制数来表示,OSTCBX的值为优先级的0~2位,OSTCBY的值为3~5位。优先级可以用OSTCBY<<3+OSTCBX来计算。OSTCBBitY和OSTCBBitX为00000001B 0000 0010B 到1000 0000B之间的8个数,Y代表在OSRdyGrp的对应位 X代表OSRdyTbl中的对应位。
任务控制块的初始化函数OSTCBInit()如下:
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList; /*从空任务控制块链表取出一个TCB */
if (ptcb != (OS_TCB *)0) {
OSTCBFreeList = ptcb->OSTCBNext; /*更新空任务控制块链表*/
OS_EXIT_CRITICAL();
ptcb->OSTCBStkPtr = ptos; /*载入任务堆栈指针*/
ptcb->OSTCBPrio = (INT8U)prio; /*载入任务优先级*/
ptcb->OSTCBStat = OS_STAT_RDY; /*置就绪标志位*/
ptcb->OSTCBDly = 0; /* 任务不进行延时等待*/
#if OS_TASK_CREATE_EXT_EN > 0 //如果利用OSTaskCreateExt()创建任务
ptcb->OSTCBExtPtr = pext; /*存储TCB扩展指针*/
ptcb->OSTCBStkSize = stk_size; /* 存储堆栈长度*/
ptcb->OSTCBStkBottom = pbos; /*存储栈底指针 */
ptcb->OSTCBOpt = opt; /*存储选项参数 */
ptcb->OSTCBId = id; /* 存储任务标识符*/
#else
pext = pext; /*预防编译器报警*/
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0
ptcb->OSTCBDelReq = OS_NO_ERR; //如果任务是自删除任务,则置TCB删除申请
#endif
ptcb->OSTCBY = prio >> 3;
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = prio & 0x07;
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
#if OS_EVENT_EN > 0
ptcb->OSTCBEventPtr = (OS_EVENT *)0; #endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) && (OS_TASK_DEL_EN > 0)
ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0;
#endif
#if (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
ptcb->OSTCBMsg = (void *)0;
#endif
#if OS_VERSION >= 204
OSTCBInitHook(ptcb); //用户自定义函数
#endif
OSTaskCreateHook(ptcb); /*用户自定义函数*/
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb; //将TCB挂载到优先级相对应的按优先级查找TCB的数组中
ptcb->OSTCBNext = OSTCBList; /*更新TCB链表 */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; /*使任务处于就绪状态*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}