曹雷欣 孔祥坤
【关键词】uC/OS-II任务;多任务切换;信号量机制;任务TCB;Task控制块
(一)uC/OS-II其任务建立一般在多任务运行前,子任务也可以动态地被运行着的任务建立。如果该任务被另一个未运行任务所建,两个任务中高优先级任务将取得CPU的控制权。
(二)一个任务被创建后,可能处在以下五种状态:
休眠态(Dormant State)指任务被调到内存中准备运行,但还没有被uC/OS-II内核转让管理权时的状态。
任务就绪态(Ready State)指将要运行的任务准备就绪,即将开始运行。
任务等待态(Waiting State)指等待一段时间然后将会被调用进入运行状态。
中断服务状态(Interrupt Service State)指任务因高优先级任务抢占被中断执行,被迫进入中断服务例程的状态。
执行状态(Executive state)指任务正处于运行的状态。
(一)就绪态、运行态、等待态转为休眠态的切换方法:
一个任务处于就绪状态或运行状态或等待状态时,要想切换到休眠态需要调用OSTaskDel()返回到休眠态。
(二)由睡眠态进入就绪态的方法
任务处于休眠态要调用OSTaskCreate()或OSTaskCreateExt0函数,这样就可以把任务交给uC/OS-II内核管理,同时进入就绪态状态( Ready State),准备运行。
(三)由运行态进入等待状态的方法
任务要想进入运行态需要OSTimeDly()或OSTimeDlyHMSMO函数方法,任务进入运行态将等待下一个优先级更高的、达到了就绪态的任务到来,并转入CPU控制权。
将运行态的任务变换成等待某一事件发生的等待态(WAITING State)需要调用OSSemPend(),OSMboxPend0,或OSQPend0函数。挂起任务(PendTask),由于等待事件或所需资源没有就绪,CPU的控制权会被下一个优先级更高的、提前进入就状态绪态的任务抢占。
(四)由等待状态进入就绪态
uC/OS-II系统服务函数OSTimeTick()发现处于等待态的任务等待时间片耗尽后,会切换任务进入到就绪态。之前被挂起的任务发现等待的事件发生了(所需资源被释放),也将进入就绪态。这种消息可能来自系统的中断服务例程或自某一个任务。
(五)运行状态与中断状态之间的相互转换
一个处于运行状态的任务被内部或外部的中断了就进入了中断服务态(ISR)。子中断服务程序当执行的任务被挂起时会交还CPU的使用权。
处于中断状态的任务当中断服务子程序执行完返回后系统将会调用OSlnext()函数使优先级最高的并进入就绪状态的任务得以运行。
(六)就绪状态与运行状态的相互转换
任务在处于就绪态时,当系统调度OSStart()函数执行时,就进入了运行态。
一个正在运行的状态由于某种原因它的CPU使用权被剥夺时就进入了就绪状态。
(七)任务状态之间相互转换的关系在uC/OS-II中如图1所示。
(一)多任务之间通信,在uC/OS-II系统中主要考虑了两个问题:
Task(任务)之间可以共享资源,但在资源访问上是一种互斥关系,是一种独占式的共享。正在执行的任务在没有释放独占资源时,其他任务则只能等待;当任务获得任务执行所需事件及资源都具备后,等待当前任务结束会顺序执行,否则任务只能循环等待。
(二)任务间的通信
1、事件
uC/OS-II任务之间实现通信的媒介包括信号量、消息队列和消息邮箱。事件之间的通信包括发送事件、请求事件,其操作就是任务把信息从一个事件发送到另一个事件上。请求事件又被叫做读取事件,或者称之为等待事件。
2、信号量
信号量本质上说是事件,是uC/OS-II中实现任务间通信的一种机制设置。信号量的机制中有一种二值形式的信号量(两种状态),可以实现资源的独占式共享,又叫做互斥型的信号量,其余信号量机制还有计数式信号量等。
在信号量机制的设计上,是基于打破请求保持的死锁条件的考虑。为防止任务间因相互持有对方任务执行所必需的资源,且继续请求其他所需资源,造成多个任务都无法得到执行的情况发生,因此在等待信号量的时间上规定了一个时限,即等待时限。当任务等待时间超过规定,仍未等到这个信号,就强制命令该任务停止等待,释放所保持资源。
在以优先级作为调度依据的可抢占式实时操作系统uC/OS-II的核心代碼中,优先级的高低完全决定了任务获得CPU使用权的先后顺序。系统依据信号量获得与否,决定调度任务能否被运行。因此在某些使用信号量同步机制的实时任务中,有两个限制任务能否运行的条件:一是任务优先级的高下:二是正在等待信号量的获得与否。
3、消息邮箱
uC/OS-II实时操作系统中,多任务间采用消息邮箱传递信息,这种方法很好地实现了任务间的通信。通常,多任务系统的内存中有共享存储区,以作消息缓冲。对缓冲区的数据存取操作,可以实现任务间的消息传递。消息邮箱的数据类型是存取消息的指针型数据结构。
4、消息队列
消息邮箱可以在设计的时候设计为多个也可以为单个。多消息邮箱以指针数组的形式实现,一个个数组元素里存放着消息缓冲区指针的数组型数据结构,就是所谓的消息队列,消息缓冲区以指针数组的方式一次实现多个消息传递。
(一)任务就绪表
uc/OS-II总是运行进入就绪状态任务中优先级最高的那一个。所有任务的就绪态都存放于在一个叫就绪表( Ready List)的数据结构中。就緒表是提供给任务调度使用的,无需用户干预,系统会从若干个不同优先级的任务中找出相应就绪的任务进入运行状态。
(二)就绪表的构成
就绪表由OSRdyGrp和OSRdyTbl[]构成,在OSRdyGrp中,任务按优先级分组,8个任务一组,当前系统最多有64个优先级,所以OSRdyGrp有8个有效位,一共可以对应8组任务。每一位对应一组任务,第O位对应的就是优先级0-7的任务,这8个任务之间是“或”的关系,只要一个任务处于就绪状态,那么OSRdyGrp的第O位就是1,如下图2所示。
(三)事件的等待任务列表
uC/OS-II实现事件管理的功能有两个:一是对等待该事件或资源的所有任务,依据其到来的先后,对任务进行记录和排序;二是对正在等待执行的所有任务,允许其在一定时间内等待,而不是一直等待。uC/OS-II采用了与任务就绪表类似的方法实现对于等待事件任务的记录和排序,定义一个8位整型( INT8U)有记录功能的数组OSEventTbl[],作为等待任务的记录表。uC/OS-II在事件机制上,还定义另一个8位整型( INT8U)的变量OSEventGrp,作为等待任务记录表中的任务组。
任务控制块( TCB)的成员函数OSTCBDly可以记录任务的等待时件,在下个时钟节拍到来时,中断服务程序将会自动进行计数。当出现超时任务时,TCB则将会把记录从事件等待任务表中删除,并重新恢复任务进入就绪态。
(四)任务控制块OS-TCB与任务栈OS_STK
OS-TCB这种数据结构,就用来描述任务所具有的某些属性的,uC/OS-II将会用它来对各个任务进行处理:
1、当前任务被建立时,任务控制块将被初始化;
2、当前任务CPU使用权被抢占后,TCB将会保存任务的状态;
3、当前任务重新获得CPU使用权时,为了确保任务能够从中断之时继续执行下去,TCB会把保存的数据装载到要执行任务的堆栈中进行恢复。
由于OS_TCB只有在任务创建之后才有意义,所以其全部驻留在RAM当中。
任务堆栈(OS_STK)在uC/OS-II中的作用是保存任务运行时涉及到的资源,例如使用到的寄存器、调用这个任务的返回地址,或者是任务内部函数嵌套时用到的返回地址保存参数等。
(五)事件控制块ECB
uC/OS-II中有关任务调度技术上,设置了一个事件控制块( ECB)。ECB的能够把事件的等待任务表和与事件有关的信息捆绑在一起,并定义为事件。ECB这种结构是用来存储诸如信号量、邮箱(消息邮箱)和消息队列等事件。
从OSEventTbI[OS_EVENT_TBL_SIZE]其定义来看,其实际为指针数组,其数据类型和任务就绪表在定义格式上颇似。程序中,所有任务按照其优先级高低,各自在表中占据一个二进制位,以二值(1或者0)的形式来记录。该位标记对应的任务是否是正在等待事件的任务,标记任务状态的这张表又被称为任务等待表。
1、事件控制块的基本函数操作
TCB基本操作函数,其函数方法,都定义在内核文件OS_CORE.C中,供信号量事件,消息邮箱事件,消息队列事件来调用。
(1)事件控制块的初始化函数
#if (OS_Q_EN&&(OS_MAX_QS>=2))11 0S_MBOX_EN ll OS_SEM_EN//初始化条件
void OSEventWaitListlnit (OS_EVENT *PREVENT)//指向TCB的指针OS_EVENT *PREVENT
该函数在任务调用OS***Create0函数时进行事件创建,被OS****Create0函数作为子函数调用。在此指代:Sem,Mutex,Mbox,Q。
(2)切换Task进入等待状态的函数
需要把一个任务切换成等待状态,系统会调用OSEventTaskWait0功能函数。
#if (OS_Q_EN&&(OS_MAX_QS>=2))11 0S_MBOX_EN|| OS_SEM_EN
void OSEventTaskWait (OS_EVENT*pevent)
上边的OSEventTaskWait()函数,将在任务调用函数OS***Pend0发送事件时,被OS***Pend0来调用。
(3)切换Task进入正在等待就绪状态的函数
在当前任务中,调用OSEventTaskRdy0函数把该任务在Task等待表中所置位清0,等切换出等待状态后,任务就绪表中把该任务所在的对应,设置为1,新一轮的任务的调度工作将启动。
(六)空事件控制块链表
空事件控制块链表其数据结构实现上,与管理任务控制块的实现方法颇有相似,uC/OS-II把事件控制块也组织成为两条链表来管理。
uC/OS-II在系统完成初始化后,会在初始化函数OSlnit0中,按照应用程序使用事件总数的最大值,创建OS_MAX_EVENTS个空事件块,并以成员OSEventPtr作为链接指针组成单向链表。
本文简要介绍了uG/OS-II的多任务切换方法和uG/OS-II的任务之间的通信机制。本文初步的对uC/OS-II的初步探究揭示了实时操作系统( RTOS)的多任务的一般实现途径,将对研究其他操作系统的多任务的实现和管理机制的研究有一定的借鉴作用。