μC/OS-II最小资源分配单元是任务。
任务状态
μC/OS-II中任务有5种状态,分别为睡眠态,就绪态,运行态,就绪态,等待状态,中断服务态。睡眠态指任务还没有创建或者创建后被删除。就绪态是指任务准备运行,等待CPU使用权。运行态指获得CPU使用权,任务执行。等待状态是指正在运行的任务调用等待或延时函数时进入的状态。中断服务态是指运行态被打断,进入中断服务程序。
任务控制块
任务控制块是一个数据结构,用于保存任务的参数和状态。
typedef struct os_tcb {
//OS_STK定义为32位无符号数据,该行定义当前任务所分配的堆栈的栈顶指针
//(该栈顶指针是指任务切换后自动保存的r0-r15等一系列数据后的堆栈指针)
//对于堆栈,ucos可以对每一个任务分配任意大小的堆栈
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
#if OS_TASK_CREATE_EXT_EN > 0//如果使能了扩展任务控制块,则定义以下数据
//扩展任务块的指针,扩展任务快的引入,使得可以在不改变ucos源码的情况下,
//加入扩展功能,此外如果需要使用该功能,则需在OS_CFG.H(ucos配置文件)
//中将OS_TASK_CREATE_EXT_EN 置1,允许建立任务函数扩展
void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
//指向任务堆栈的栈底(就是数据最后进入的地址),如果堆栈增长方式是递增的,那么他执向堆栈的最高地址
//反之指向最低地址,该参数在使用OSTaskStkChk函数时需要调用
OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
//该参数是任务堆栈的大小,对于堆栈大小是指栈中所能容纳的指针数目,而不是字节数目,假设堆栈容量为1000,
//如果地址宽度为32位,那么堆栈包含4000个字节。但是其容量是1000
INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
//选择项,支持3种选择e:
//OS_TASK_OPT_STK_CHK,该参数用于告知TaskCreateExt函数在建立任务时对堆栈进行检查(不定义这个ucos不会进行检查)
//OS_TASK_OPT_STK_CLR,在任务建立 时将任务栈清零,至于在需要使用栈检验功能时才将栈清零
//OS_TASK_OPT_SAVE_FP,通知任务需要做浮点运算
INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
//用于存储任务的识别码,现在还没使用,感觉他会发展成为linux的pid
INT16U OSTCBId; /* Task ID (0..65535) */
#endif
//任务控制块双向链表所需变量,分别指向给任务的后一个任务控制块和前一个任务控制块
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
#if OS_EVENT_EN || (OS_FLAG_EN > 0)
//如果使能了事件,定义指向事件控制块的指针。
OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
#endif
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
//如果定义了队列,消息邮箱,信号量系统资源,则使用该指针指向所要传递的消息
void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
#endif
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
//指向事件标志节点的指针
OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */
#endif
//准备运行的事件标志任务
OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
#endif
//任务延时时间,或者等待事件发生的超时时间,在时钟每发生一次中断的时候,时钟街拍函数OSTimeTick
//将通过任务控制块的链表访问该变量,并将其减1,刷新变量
INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
//用于描述任务的状态
INT8U OSTCBStat; /* Task status */
//挂起状态
INT8U OSTCBStatPend; /* Task PEND status */
//任务优先级
INT8U OSTCBPrio; /* Task priority (0 == highest) */
//表示任务就绪表中X轴的位置,相当于说明该优先级是某一组的哪一位元素
INT8U OSTCBX; /* Bit position in group corresponding to task priority */
//表示任务就绪表中Y轴的位置,相当于说明该优先级是哪一组的元素
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
#if OS_LOWEST_PRIO <= 63
//说明该优先级对应的OSTCBTBL[prio & 0x7]中元素赋值
INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
//说明该优先级对应的OSRdyGrp的元素
INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */
#else
INT16U OSTCBBitX; /* Bit mask to access bit position in ready table */
INT16U OSTCBBitY; /* Bit mask to access bit position in ready group */
#endif
#if OS_TASK_DEL_EN > 0
//该参数表明任务是否需要删除
INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */
#endif
#if OS_TASK_PROFILE_EN > 0
//用于切换任务的时钟计数
INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */
//统计滴答时钟
INT32U OSTCBCyclesTot; /* Total number of clock cycles the task has been running */
//快速启动任务计数
INT32U OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption */
//指向开始任务堆栈的指针
OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */
//堆栈中使用的字节数量
INT32U OSTCBStkUsed; /* Number of bytes used from the stack */
#endif
//任务名
#if OS_TASK_NAME_SIZE > 1
INT8U OSTCBTaskName[OS_TASK_NAME_SIZE];
#endif
} OS_TCB;
关于任务还有几个全局变量
OS_EXT OS_TCB *OSTCBCur; //指向当前运行任务TCB的指针
OS_EXT OS_TCB *OSTCBFreeList; //执行下一个可用TCB指针
OS_EXT OS_TCB *OSTCBHighRdy; //指向最高优先级的TCB的指针
OS_EXT OS_TCB *OSTCBList; //指向所创建TCB链表的指针
OS_EXT OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1];//指向所创建TCB表的指针
OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; //TCB表
在系统初始化时,默认在RAM中开辟OS_MAX_TASKS + OS_N_SYS_TASKS(最大允许创建的任务数+系统任务)大的0S_TCB类型的数组,此时结构如下所示:
创建系统任务后:
任务创建与删除
任务的创建通过OSTaskCreate或OSTaskCreateExt完成,任务删除通过OSTaskDel完成。
OSTaskCreate:
INT8U OSTaskCreate(void (*task)(void *p_arg),void *p_arg,OS_STK *ptos,INT8U prio)
{
OS_STK *psp;
INT8U err;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if(prio > OS_LOWEST_PRIO) /* Make sure priority is within allowable range */
{
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL(); //保存全局中断标志,关中断
if(OSIntNesting>0) /* Make sure we don't create the task from within an ISR */
{
OS_EXIT_CRITICAL(); //恢复全局中断标志
return (OS_ERR_TASK_CREATE_ISR);
}
if(OSTCBPrioTbl[prio]==(OS_TCB *)0) /* Make sure task doesn't already exist at this priority */
{
OSTCBPrioTbl[prio]=OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL(); //恢复全局中断标志
psp = OSTaskStkInit(task,p_arg,ptos,0); /* Initialize the task's stack *///建立任务堆栈
err = OS_TCBInit(prio,psp,(OS_STK *)0,0,0,(void *)0,0);//从空闲的OS_TCB池获得并初始化一个OS_TCB
if(err == OS_ERR_NONE)
{
if(OSRunning==OS_TRUE)
{ /* Find highest priority task if multitasking has started */
OS_Sched();//任务调度
}
}
else
{
OS_ENTER_CRITICAL(); //保存全局中断标志,关中断
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL(); //恢复全局中断标志
}
return (err);
}
OS_EXIT_CRITICAL(); //恢复全局中断标志
return (OS_ERR_PRIO_EXIST);
}
OSTaskCreateExt:
INT8U OSTaskCreate(void (*task)(void *p_arg),void *p_arg,OS_STK *ptos,INT8U prio)
{
OS_STK *psp;
INT8U err;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if(prio > OS_LOWEST_PRIO) /* Make sure priority is within allowable range */
{
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL(); //保存全局中断标志,关中断
if(OSIntNesting>0) /* Make sure we don't create the task from within an ISR */
{
OS_EXIT_CRITICAL(); //恢复全局中断标志
return (OS_ERR_TASK_CREATE_ISR);
}
if(OSTCBPrioTbl[prio]==(OS_TCB *)0) /* Make sure task doesn't already exist at this priority */
{
OSTCBPrioTbl[prio]=OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL(); //恢复全局中断标志
psp = OSTaskStkInit(task,p_arg,ptos,0); /* Initialize the task's stack *///建立任务堆栈
err = OS_TCBInit(prio,psp,(OS_STK *)0,0,0,(void *)0,0);//从空闲的OS_TCB池获得并初始化一个OS_TCB
if(err == OS_ERR_NONE)
{
if(OSRunning==OS_TRUE)
{ /* Find highest priority task if multitasking has started */
OS_Sched();
}
}
else
{
OS_ENTER_CRITICAL(); //保存全局中断标志,关中断
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL(); //恢复全局中断标志
}
return (err);
}
OS_EXIT_CRITICAL(); //恢复全局中断标志
return (OS_ERR_PRIO_EXIST);
}
OSTaskDel:
INT8U OSTaskDel (INT8U prio)
{
#if OS_EVENT_EN
OS_EVENT *pevent;
#endif
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FLAG_NODE *pnode;
#endif
OS_TCB *ptcb;
INT8U y;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (OSIntNesting > 0) { /* See if trying to delete from ISR */
return (OS_ERR_TASK_DEL_ISR);
}
if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */
return (OS_ERR_TASK_DEL_IDLE);
}
#if OS_ARG_CHK_EN > 0
if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */
if (prio != OS_PRIO_SELF) {
return (OS_ERR_PRIO_INVALID);
}
}
#endif
OS_ENTER_CRITICAL();
if (prio == OS_PRIO_SELF) { /* See if requesting to delete self */
prio = OSTCBCur->OSTCBPrio; /* Set priority to delete to current */
}
ptcb = OSTCBPrioTbl[prio];
if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NOT_EXIST);
}
if (ptcb == OS_TCB_RESERVED) { /* Must not be assigned to Mutex */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_DEL);
}
y = ptcb->OSTCBY;
OSRdyTbl[y] &= ~ptcb->OSTCBBitX;
if (OSRdyTbl[y] == 0) { /* Make task not ready */
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
#if OS_EVENT_EN
pevent = ptcb->OSTCBEventPtr;
if (pevent != (OS_EVENT *)0) { /* If task is waiting on event */
pevent->OSEventTbl[y] &= ~ptcb->OSTCBBitX;
if (pevent->OSEventTbl[y] == 0) { /* ... remove task from ... */
pevent->OSEventGrp &= ~ptcb->OSTCBBitY; /* ... event ctrl block */
}
}
#endif
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
pnode = ptcb->OSTCBFlagNode;
if (pnode != (OS_FLAG_NODE *)0) { /* If task is waiting on event flag */
OS_FlagUnlink(pnode); /* Remove from wait list */
}
#endif
ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from updating */
ptcb->OSTCBStat = OS_STAT_RDY; /* Prevent task from being resumed */
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
if (OSLockNesting < 255u) { /* Make sure we don't context switch */
OSLockNesting++;
}
OS_EXIT_CRITICAL(); /* Enabling INT. ignores next instruc. */
OS_Dummy(); /* ... Dummy ensures that INTs will be */
OS_ENTER_CRITICAL(); /* ... disabled HERE! */
if (OSLockNesting > 0) { /* Remove context switch lock */
OSLockNesting--;
}
OSTaskDelHook(ptcb); /* Call user defined hook */
OSTaskCtr--; /* One less task being managed */
OSTCBPrioTbl[prio] = (OS_TCB *)0; /* Clear old priority entry */
if (ptcb->OSTCBPrev == (OS_TCB *)0) { /* Remove from TCB chain */
ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0;
OSTCBList = ptcb->OSTCBNext;
} else {
ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext;
ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev;
}
ptcb->OSTCBNext = OSTCBFreeList; /* Return TCB to free TCB list */
OSTCBFreeList = ptcb;
#if OS_TASK_NAME_SIZE > 1
ptcb->OSTCBTaskName[0] = '?'; /* Unknown name */
ptcb->OSTCBTaskName[1] = OS_ASCII_NUL;
#endif
OS_EXIT_CRITICAL();
if (OSRunning == OS_TRUE) {
OS_Sched(); /* Find new highest priority task */
}
return (OS_ERR_NONE);
}
空闲任务与统计任务
空闲任务和统计任务为系统任务。空闲任务为最低优先级OS_LOWEST_PRIO,在没有其他任务就绪时运行,不运行时永远处于就绪态。统计任务优先级为OS_LOWEST_PRIO - 1,可计算CPU利用率.
任务调度
void OS_Sched(void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL(); //保存全局中断标志,关中断
if(OSIntNesting==0) /* Schedule only if all ISRs done and ...*/
{
if(OSLockNesting==0) /* ... scheduler is not locked */
{
OS_SchedNew(); //计算就绪任务里优先级最高的优先级,结果保存在OSPrioHighRdy里
if(OSPrioHighRdy!=OSPrioCur) /* No Ctx Sw if current task is highest rdy */
{
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); //悬起PSV异常 /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL(); //恢复全局中断标志
}