uC/OS-III的任务管理
欢迎进入linuxweiyh的博客。。。
1.多任务管理就是在多个任务间调度和切换CPU使用权的过程。
2.任务的类型有两种:运行至完成型和无限循环型。
注:嵌入式系统一般使用的是无限循环类型的任务。在无限循环类型的任务中必须调用某个uC/OS-III服务函数,使该任务进入等待某个事件的状态。
3.任务的创建参考另一篇文章《uC/OS-III任务创建函数》
4.有关任务优先级的分配,这里说了一种方法:首先按照任务的关键性进行划分,分为关键任务和非关键任务。关键任务的优先级按照紧迫性来进行优先级的划分,而非关键任务可以遵照单调执行率调度发(RMS)进行划分,即执行率越高,优先级越高。
5.有关栈空间大小的确定,这里说的手动计算栈空间大小的方法很不现实,所以采用零一种方法,先分配很大的栈空间,然后,通过监视任务运行时栈的使用情况,就可以知道栈的实际使用空间。
这里摘抄自一段书上的原话,个人认为很重要:由于堆栈溢出很常见,并且会造成一些很奇怪的现象,因此,在开发和测试产品时,要一直监视任务在运行时的堆栈使用量。实际上,只要当有人提出她(或他)的应用程序的运行结果很奇怪时,首先就应该怀疑给任务分配的栈空间是不是太小了。
注:嵌入式系统的一个通用原则:不使用递归调用。
6.关于任务栈溢出检测,这里给出了一下几种方法。
(1)使用MMU(存储器管理单元)或MPU(存储器保护单元)
(2)使用具有堆栈溢出检测功能的CPU
(3)基于软件的堆栈溢出检测
(4)计算空闲堆栈空间的数量
注:以上4种方式,目前还未使用过其中任何一种。在uC/OS-III中提供了最后一种,具体的说明详见以后更新。
7.任务的用户态
从用户角度来看,任务的状态有5种,休眠态、就绪态、运行态、等待态、中断服务态。
(1)休眠态
书上有关休眠态的描述是任务已经存在于存储器中,但还不受uC/OS-III的管理。本人觉得这里的描述太啰嗦,所以给出最简单的描述,任务已经存在了,但是还没有添加到uC/OS-III任务表的任务表中。
当不再需要某一个任务时,用户可以调用OSTaskDel()把任务从任务就绪表中删除。
(2)就绪态
剩余的4种状态都可以进入到就绪态,uC/OS-III是通过任务就绪表记录就绪的任务。
(3)运行态
有关运行态的描述,有这么一句话,最重要的就绪任务进入运行态,这句话体现了uC/OS-III实时系统的最根本的调度依据–只有优先级最高的就绪任务才能获得CPU的使用权(对于单核CPU来说)。
当应用程序调用OSStart()时,或者当uC/OS-III调用OSIntExit或者OS_TASK_SW()时,uC/OS-III会切换到最重要的就绪任务,使其获得CPU的使用权。
(4)等待态
等待态的任务会被放入与所等待的事件相对应的等待表中。处于等待态的任务不消耗任何CPU的时间。
当任务等待的事件发生时,任务被重新放回到任务就绪表中,并且uC/OS-III会判断这个刚就绪的任务是否是最重要的任务。如果是,则当前正在运行任务的CPU使用权会被剥夺(放入任务就绪表),而刚就绪的任务会获得CPU的使用权。
注意:调用函数OSTaskSuspend()可无条件阻塞一个任务的运行,被阻塞的任务实际上并没有等待某个时间的发生。只有通过调用另外一个函数OSTaskResume(),才能使被阻塞的任务恢复运行。
(5)中断服务态
中断服务态其实就是发生中断时,当前正在运行的任务被挂起,CPU开始执行中断服务子程序ISR。ISR是任务等待的一种典型事件。
一般来说,ISR应当只是通知任务某个时间已经发生,而事件的处理则交个任务。ISR应当尽可能的短,中断处理的大部分工作应当在uC/OS-III可管理的任务级来做。ISR仅仅允许调用与Post相关的这一类函数(即OSFlagPost()、OSQPost()、OSSemPost()、OSTaskQPost()和OSTaskSemPost())。唯一一个不允许ISR调用的Post函数是OSMutexPost()。
中断能中断另外一个中断,这被称为中断嵌套。
注:状态转换图请参考《嵌入式实时操作系统uC/OS-III》P64。
8.任务在uC/OS-III内部的状态
在uC/OS-III内部,任务控制块OS_TCB中的成员TaskState记录任务的状态。uC/OS-III内部任务的状态总共有8种,延时、就绪、带超时检测的等待、等待、被挂起、延时且被挂起、带超时检测的等待且被挂起、等待且被挂起。
注意,在uC/OS-III内部并没有任务用户态中的运行态、休眠态和中断服务态,其中关于没有休眠态的原因很好理解,因为uC/OS-III内部不知道休眠态的任务的存在。而其他两种,目前本人也暂时还不理解。
注:详细的状态转换请参考《嵌入式实时操作系统uC/OS-III》P66。
9.任务控制块OS_TCB
任务控制块TCB是内核使用的一种数据接口,用来维护任务相关的信息。每个任务TCB。
uC/OS-III就是通过任务控制块TCB管理任务的。
下面是任务控制块TCB的源码,来自文件os.h的878行。
struct os_tcb {
CPU_STK *StkPtr; // 指针变量,指向任务当前的栈顶
void *ExtPtr; // 指向用户可定义的数据区,可以根据需要扩展TCB,其值由传递给OSTaskCreate()的参数p_ext设定
CPU_STK *StkLimitPtr; // 设置堆栈延伸的限制位置,有传递给OSTaskCreate()的参数stk_limit设定
// 在uC/OS-III内部,任务控制块TCB构成的是OS_TCB双向链表
OS_TCB *NextPtr;
OS_TCB *PrevPtr;
// 正在延时或在指定时间内等待事件的所有任务构成是双向的OS_TCB链表
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
OS_TICK_SPOKE *TickSpokePtr; // 指向任务节拍轮的根条幅
CPU_CHAR *NamePtr; // 任务的名字
CPU_STK *StkBasePtr; // 任务堆栈的基地址,它的值永远是&???Stk[0]
OS_TASK_PTR TaskEntryAddr; // 任务代码的入口地址
void *TaskEntryArg; // 创建任务时传递个任务的参数的数值
OS_PEND_DATA *PendDataTblPtr; // 指向了一个表,该表包含了任务等待的所有事件对象的信息
OS_STATE PendOn; // 表示任务正在等待的事件的类型
OS_STATUS PendStatus; // 表示等待的结果
OS_STATE TaskState; // 任务的当前状态
OS_PRIO Prio; // 任务的当前优先级
CPU_STK_SIZE StkSize; // 任务堆栈的大小
OS_OPT Opt; // 任务创建时传递给OSTaskCreate()的可选参数options的数值
OS_OBJ_QTY PendDataTblEntries; // 表示任务同时等待的事件对象的数目,与.PendDataTblPtr配合使用
CPU_TS TS; // 存储事件发生时的时间戳
OS_SEM_CTR SemCtr; // 任务內建信号量的计数器
// 与任务等待延时有关
OS_TICK TickCtrPrev;
OS_TICK TickCtrMatch;
OS_TICK TickRemain;
// 与时间片的分配相关
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr;
#if OS_MSG_EN > 0u
void *MsgPtr; // 指向接收到的消息
OS_MSG_SIZE MsgSize; // 消息的长度
#endif
#if OS_CFG_TASK_Q_EN > 0u
OS_MSG_Q MsgQ; // 任务內建消息队列
#if OS_CFG_TASK_PROFILE_EN > 0u
CPU_TS MsgQPendTime; // 一条消息到达所花费的时间
CPU_TS MsgQPendTimeMax; // 一条消息到达所花费的最长时间
#endif
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OS_REG RegTbl[OS_CFG_TASK_REG_TBL_SIZE]; // 任务寄存器,用来存储任务专用的信息
#endif
#if OS_CFG_FLAG_EN > 0u
OS_FLAGS FlagsPend; // 包含任务等待的那些事件的标志位
OS_FLAGS FlagsRdy; // 记录任务在等待的事件标志中有哪些已经就绪
OS_OPT FlagsOpt; // 等待的类型
#endif
#if OS_CFG_TASK_SUSPEND_EN > 0u
OS_NESTING_CTR SuspendCtr; // 记录任务被挂起的次数,与OSTaskSuspend()和OSTaskResume()的使用相关
#endif
#if OS_CFG_TASK_PROFILE_EN > 0u
OS_CPU_USAGE CPUUsage; // 表示任务的CPU利用率
OS_CTX_SW_CTR CtxSwCtr; // 记录任务执行的频繁程度
CPU_TS CyclesDelta; // 记录任务一次运行期间占用CPU的时间
CPU_TS CyclesStart; // 表示任务每次开始运行时的时间戳
OS_CYCLES CyclesTotal; // 表示任务总的执行时间
OS_CYCLES CyclesTotalPrev; // 表示任务前一次总的执行时间
CPU_TS SemPendTime; // 信号量的发送所花费的时间
CPU_TS SemPendTimeMax; // 信号量发送到一个任务所花费的最长时间
#endif
#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
CPU_STK_SIZE StkUsed; // 已使用的栈空间大小
CPU_STK_SIZE StkFree; // 未使用的栈空间大小
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_TS IntDisTimeMax; // 任务的最大中断关闭时间
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
CPU_TS SchedLockTimeMax; // 记录任务给调度器上锁的时间
#endif
// 与调试相关
#if OS_CFG_DBG_EN > 0u
OS_TCB *DbgPrevPtr;
OS_TCB *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
};
任务控制块OS_TCB中成员的分类总结:
任务本身有关的成员:NamePtr、TaskState、Prio、TaskEntryAddr、TaskEntryArg、ExtPtr
任务栈有关的成员:StkPtr、StkLimitPtr、StkBasePtr、StkSize
任务链表有关的成员:
任务延时有关的成员:
时间片有关的成员:
內建信号量有关的成员:
內建消息队列有关的成员:
事件有关的成员:
事件标志组有关的成员:
注:有关以上部分的总结当用到的时候再填充。