uC/OS看到现在,让我感叹这个系统真的是用空间换取时间的典范,把所有的东西都先建立好,通过查表或者是保存一大堆变量,目的就是要用的时候就能随手拿到,不用再进行计算了,感叹一下。
先列一下比较重要的几个量(MAX_TASKS表示最大任务个数,LOWEST_PRIO指最低优先级数):
OSRunning:用于标识多任务环境是否已经开启运行,在OSStart()函数里启动任务后就置为True。
OSIntNesting:用于标识中断嵌套层数。产生一次中断可以调用OSIntEnter()函数使该值自增1,处理完一次中断再调用OSIntExit()自减1。
OS_TCB:一个结构体变量,每建立一个任务都会为该任务指定一个OS_TCB,包含该任务的栈顶指针,任务优先级,任务状态字,延时节拍等所有相关信息。每个任务的OS_TCB都存在于一个双向链表内,并使变量OSTCBList指向最后建立的那个任务的OS_TCB。
OS_STK(INT32U):任务堆栈,每个任务都有一个自己的堆栈空间,用于保存寄存器,状态值,和任务的入口地址。这个堆栈与系统栈没有什么关系,只是用户为一个任务分配的存储任务信息的空间,只需要是一个连续的存储空间即可。
OSRdyGrp,OSRdyTbl[ ]:用于标识就绪的任务。uC/OS根据优先级唯一确定一个任务,也即一个优先级只能分配给一个任务。OSRdyTbl[ ]里保存(MAX_TASKS/8+1)个字节,每个字节又有8个Bit,从最低位开始分别对应0~LOWEST_PRIO优先级的任务,任务就绪时就将该任务的优先级所在Bit置1,否则清0。
OSRdyGrp完全是为了找到最高优先级方便而设立的,将OSRdyTbl[ ]里每个字节代表的8个任务划为一个组,只要某一组内有就绪任务时,OSRdyGrp的相应位就置1。比如,只要OSRdyTbl[0]不为0,则OSRdyGrp的第0位就置1,依次类推。
OSPrioCur:这是一个整型数,表示当前执行任务的优先级数。
OSPrioHighRdy:一个整型数,表示当前最高级别的就绪任务的优先级数。
OSTCBHighRdy:这是一个指针,它指向最高优先级就绪任务的OS_TCB。
OSTCBCur:一个指针,指向当前执行任务的OS_TCB。
OSTCBTbl[ ]:初始化时建立的一块存储空间,一共建立了MAX_TASKS个OS_TCB空间。并令变量OSTCBFreeList总是指向下一个可用的OS_TCB空间。当新建立一个任务时,就将一个空的OS_TCB空间分配给它使用。
OSTCBPrioTbl[]:这是一个指针数组,用于保存就绪任务的OS_TCB地址,一共有MAX_TASKS个值,依次对应0~LOWEST_PRIO优先级的任务。当某一优先级未分配给一个任务时,该数组对应元素的值为(void *)0,当某优先级的任务分配给一个处于就绪态的任务时,对应元素的内容就被写入该任务的OS_TCB所在地址。
拿到没用过的系统,首先就想知道它是怎么用的,对于一个实时操作系统,先要解决以下几个问题:
如何切换任务?
uC/OS多任务的原理是,在某个任务重新获得CPU控制权的时候,先把上一个执行的任务的所有寄存器以及它的返回地址保存起来,然后把现在要执行的任务的所有变量恢复到操作寄存器里,然后使PC指向这个任务的处理函数。而保存和恢复变量的重要媒介就是每个任务独有的堆栈空间,这点跟中断处理的过程几乎一样,重新获得CPU的任务可以看成是中断函数,原先执行的看成是被中断的函数,只不过uC/OS是模拟中断来切换任务,所有堆栈的保存和恢复要在移植不同处理器的时候根据实际情况来完成,目的只有一个,就是模拟中断的压栈和出栈操作。
uC/OS引起任务切换的功能函数主要是两个,任务级的切换是OSSched(),中断级的切换由OSIntExit()完成。而前者中真正实现切换操作的是OS_TASK_SW()宏(宏替换而已,实际上也是一个函数,),后者真正实现切换功能的是OSIntCtxSw(),这个宏如何实现,移植不同的处理器有不同的处理方法,目的仍然是,把现场处理的跟真正发生了中断一样,该保存的保存,该恢复的恢复(这个地方千万要搞明白,当初在这卡了一下,中断退出后,没调用这个函数的话会有异常哦,为什么见本类中另一篇日志)。
因此可以得到,uC/OS切换任务时可以有两种方法,第一种是拥有CPU控制权的任务主动调用函数OSTimeDly()、OSTaskSuspend()等这类函数把控制权交出来,重新分配给下一个优先级高的任务,实际上是依靠OSSched()调度任务。第二种方法是CPU产生中断,把当前任务的控制权剥夺掉,令它挂起,从而使下一优先级的任务得以执行,当然,如果中断没有令当前任务挂起,中断返回后它依然是最高优先级的话,CPU将继续被它控制。
如何查找最高优先级?
uC/OS把一个字节可能的256个值的所有优先级状况都做成一个表OSUnMapTbl[ ],比如,如果字节的值为7,即0000 0111,那么任务0,1,2都处于就绪态,最高优先级当然是0,那么OSUnMapTbl[7] == 0,因此,先通过y = OSUnMapTbl[OSRdyGrp]找到最高优先级的组y,然后通过x = OSUnMapTbl[OSRdyTbl[OSRdyGrp]]得到这个组里的最高优先级任务号x。因为uC/OS最大任务数不超过64,可以把优先级数看成是一个六位的二进制数,高三位表示所在组,低三位表示组内的号,因此y<<3+x就是找到的最高优先级数了,实在佩服OSUnMapTbl[ ]的设计。