二、任务管理
任务管理是ucos-ii操作系统的核心内容。这一章大致就以下流程来介绍和总结任务管理的相关知识。
要实现复杂任务管理,必然要定义众多数据来描述任务状态,为了精简,建立了许多不同的数据结构,所以第一步就是了解这些数据结构和构成。为了避免枯燥,我们同时要认识到每一种结构具体起到什么作用,感受其中的精妙之处,这样理解起来就更容易了。
建立完必要的数据结构,程序在OS_TCBInit()中对TCB进行初始化,在OSInit()中对操作系统的其他重要数据结构进行初始化。
操作系统在启动和运行过程中,会调用一系列任务管理相关函数,我们通过对这些函数的功能认识进一步了解任务管理的具体实现。
最后我们会再次梳理任务管理从初始化到运行过程中任务切换的整个流程。
1.要了解的数据结构
1>任务控制块TCB(Task Control Block)
任务控制块是最核心的数据结构,定义为一个结构体,每一个任务对应一个TCB,包含了堆栈指针、下一个TCB指针、事件块指针、任务状态、任务优先级等一系列信息。
2>空闲链表和就绪链表
空闲链表和就绪链表是由各个TCB构成的链表,所有已经创建任务的TCB分配到就绪链表,其他分配到空闲链表,每建立一个任务就从空闲链表取一个TCB给到就绪链表,每删除一个任务就将该任务TCB释放回空闲链表。
3>任务优先级指针表OSTCBPrioTb1[OS_LOWEST_PRIO+1]
即任务优先级指针数组,类型为指向TCB的指针,用来获取某优先级任务对应的TCB地址.例如任务优先级为5,其 TCB地址将存入OSTCBPrioTb1[5]。
4>任务堆栈
每个任务都有自己的堆栈空间,用于任务切换或者响应中断时保存CPU寄存器数据和私有数据。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。
5>任务就绪表OSRdyTab1[]和就绪组OSRdyGrp
为了快速找到当前就绪任务中最高优先级的任务设置了任务就绪表和就绪组,就绪表中每一位以1表示就绪,0表示未就绪。通过设定好的OSMapTb1[]和OSUnMapTb1[]两个常量数组和既定操作可以快速获取就绪任务中的最高优先级。具体结构如下图所示。
6>初始化
OS_TCBInit():TCB初始化函数,创建任务时会分配一个TCB,并对其进行初始化;
OSInit():操作系统的初始化函数,内部又分为多个子函数,包含了对全局变量,就绪表,就绪组,任务优先级指针表,事件标志组,内存,消息队列等的初始化。
2.主要相关函数简介
1> OS_TaskCreate() 基本任务创建;
2> OS_TaskCreateExt() 拓展任务创建;
3> OSTaskDel () 删除任务(任务返回并处于休眠状态,而不是删除代码);
4> OSTaskDelReq() 请求删除任务(一般用于删除任务前释放资源);
5> OSTaskSuspend() 挂起任务(用来暂时停止任务的执行,将其阻塞掉);
6> OSTaskResume() 恢复任务(将被挂起的任务恢复到就绪态);
7> OSTimeTick() 任务调度器,每隔一段时间(如20ms)执行一次;
8> OS_Sched() 任务切换函数,判断切换条件满足后执行OS_TASK_SW();
9> OS_TASK_SW() 汇编语言编写,压栈和退栈操作,真正转到新任务执行;
10> OSIntExit() 中断中的任务调度函数,判断切换条件满足后执行OSIntCtxSw()
11> OSIntCtxSw() 与CPU相关,实现中断程序中的任务切换
3. 任务管理和调度流程
任务状态转换如下图所示:
任务通过不同的函数调用以及当前情况在不同状态之间切换。图中创建任务,删除任务与第二节中1> OS_TaskCreate()、2> OS_TaskCreateExt()和3> OSTaskDel ()、4> OSTaskDelReq()相对应,事件等待中包含但不局限于OSTaskSuspend(),任务在等待信号量,邮箱或者系统延时时都会被阻塞进入阻塞态。等待的发生包含但不局限于OSTaskResume(),任务接收到信号量,邮箱以及延时完毕进行任务调度会从阻塞态转到就绪态。
任务运行中被中断打断会进入挂起态,中断服务程序结束后执行任务调度使当前最高优先级的就续任务得到运行。被挂起的任务仍为最高优先级,则返回。
OSTimeTick()为任务调度器,实际上是定时器中断,在每个时间片开始遍历每一个任务,将被设置了时间延时的任务延时时间减1,设置满足条件的任务进入就绪态。在发现有更高优先级的就续任务时则执行一次任务调度。
OS_Sched()执行普通任务切换(任务创建,自我删除和自我阻塞时会进行的任务调度),首先判断切换条件,若ISR未完成,调度器上锁或者当前任务即为最高优先级则不会进行切换。OS_Sched()中OS_TASK_SW()用于保存上下文,实现真正转到新任务执行。
OSIntExit()用于在时钟中断中进行任务调度,类似于OS_Sched()首先判断切换条件,若满足则执行OSIntCtxSw()进行任务切换。OSTimeTick()任务调度器就属于中断方式进行的调度。
空闲任务OS_TaskIdle()(操作系统必须的)和统计任务OS_TaskStart()(非必须)是两个系统两个自带的任务,保证CPU不会无事可干和监控CPU运行情况。
4.总结:
任务管理从任务控制块TCB等数据结构定义和初始化开始,进一步执行操作系统的初始化。多任务启动后,通过任务创建,删除,挂起,恢复等操作和其他事件信息操作使得任务在不同状态间切换。对于CPU来说,每次状态转换都伴随一次任务调度,内核会选择当前就续任务中最高优先级的任务执行,一直处于动态运转中。