注:红体字都是自己对这个概念的理解,有什么不对的欢迎拍砖哈,呵呵
一、uC/OSii 内核结构:任务管理,时间管理,中断处理,任务之间的通信与同步!
二、代码的临界区分析:
代码的临界区也称为临界区,指该代码在被处理时不能被分割,因此不能被中断打入!所以在进入该代码(临界区)时应该关中断,而执行完后再打开中断!而uC/OSii 用OS_ENTER_CRITICAL和OS_EXIT_CRITICAL这两个宏来开/关中断。而针对不同的处理器,这两个宏定义是不同的,分为三种,由OS_CRITICAL_METHOD的值来区分。一般ARM的OS_CRITICAL_METHOD=3(第三种宏定义)
三、uC/Osii的TASK(任务)
uC/Osii的任何工作都是由任务来完成的。
任务是一种无限循环的结构,每一个任务对应着一个任务级,uC/Osii有64个任务优先级,每一个优先级不能重复存在,也就是每一个优先级对应一个任务。因此可以把优先级看成一个任务的标志,事实上uC/Osii的确是这样做的。
TASK最主要的部分就是任务控制快(TCB), uC/Osii控制任务其实本质就是对任务的TCB进行控制
TCB是一种数据结构,那什么是数据结构?就是一些信息的集合体。TCB里面包含了哪些信息呢?以下进行分析:
(1)OSTCBStkPtr,它是指向该任务堆栈栈顶的指针,也就是指向堆栈起始地址。
(2)OSTCBStkBottom,是指向该任务堆栈栈底的指针,这个主要是检测堆栈剩余空间用的。
(3)OSTCBStkSize,堆栈容量。
(4)OSTCBOpt,没有搞清楚 ,只知道是OSTaskCreateExt(),才有用。
(5)OSTCBNext和OSTCBPrev,用语任务控制块OS_TCB双向链表的前后任务块连接,在调用OSTimeTick()时会使用这个前后连接的双向链表,来刷新各个任务的任务延迟变量OSTCBDly(马上对它说明)。每一个任务TCB建立时,会连接到该连表中,在删除任务时也会从中删除。(除了睡眠状态,任务的控制块始终连接在这个链表)
(6)OSTCBDly,当需要把某任务延迟若干时钟接拍时,或者因为等待某个事件发生而挂起任务时,都需要这个变量。其实任务时延函数OSTimeDly()的作用就是 将任务从就绪列表移除(移除方法后面会讲)并且对这个变量赋值(要延迟的节拍数)。OSTimeTick()会每隔一个节拍就通过OS_TCB双向列表(第5条讲过的)对每一个TCB的OSTCBDly进行查看,如果OSTCBDly为0不做操作继续检查下一个TCB。
当OSTCBDly非0,那么OSTimeTick()对OSTCBDly得值减一,如果做完减一后OSTCBDly值为0则表明该任务延时结束,将该任务添加到就绪队列。然后继续下一个TCB。
重要(7)OSTCBPrio、该任务的优先级。个人感觉uC/Osii就是通过这个来区别不同的TCB,也就是OSTCBPrio就是这个任务的标志或者说是名字。
(8)OSTCBX OSTCBY OSTCBBitX OSTCBBitY、 长得很像啊,容易搞混,其实这四个给一个作用那就是分解Prio这个值,从而加速任务进入就绪的过程或者进入等待事件发生状态的过程。那肯定有人就要疑惑为什么能加速呢,我们来慢慢分析下:
首先让任务进入就绪具体的操作就是将该任务的OSTCBPrio写入就绪表中OSRdyGrp和OSRdyTbl[ ]。仅此而已,所以这也印证了在第7条中我说过OSTCBPrio就是这个任务的标志。而将OSTCBPrio写入就绪表OSRdyGrp和OSRdyTbl[ ]并不是把这个值直接写入就完了,而是要通过 OSRdyGrp |=OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |=OSMapTbl[prio & 0x07];这个计算式。那么为了让任务能更快的进入就绪状态
我们就事先将OSMapTbl[prio>>3] OSMapTbl[prio & 0x07]算出来,而OSTCBX OSTCBY OSTCBBitX OSTCBBitY就是存储这个些值的变量。
进入进入等待事件发生状态的过程与之类似,就不讲了(累死了。。)
(10)OSTCBEventPtr、指向时间控制快,是TCB和ECB联系在一起。
四、任务的五种状态:
(只有休眠状态,没有TCB}
休眠态(dormant):指任务驻留在程序空间中,还没有交给内核管理,也就是任务没有分配TCB 。把任务交给内核管理是通过调用OSTaskCreate( )或OSTaskCreatExt( )实现的,为其分配TCB
就绪(Ready):当任务一旦建立,这个任务就处于就绪态准备运行。任务可以动态的被另一个程序建立,也可以在系统运行开始之前建立。通过调用OSTaskDel( )使任务返回到休眠态。就绪态的任务都放在就绪列表中。在任务调度时,指针OSTCBHighRdy指向优先级最高的就绪任务,也就是立刻就要运行的任务。
任务处于就绪队列两个条件:首现要该任务被分配了TCB。其次是要将该任务的优先级(prio)写进绪表中OSRdyGrp和OSRdyTbl[ ],具体办法如下:
OSRdyGrp |=OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |=OSMapTbl[prio & 0x07];
运行态:优先级最高的任务获得CUP,该任务就处于运行状态,指针OSTCBCur指向正在运行的任务。
。 一旦有比这个任务优先级更高的任务出现,uC/OSii 就会通过OSSched()进行任务调度,将现在处于就绪状态且优先级最高的任务交给CPU处理。
等待或挂起(Pending):正在运行的任务由于调用延时函数OSTimeDly( )、OSTimeDlyHMSM( )(将自身延迟)或等待某一事件的发生时,调用以下几个函数:OSFlagPend()、OSSemPend(),OSMutexPend()、OSMboxPend(),如果该事件发生了,那么该任务继续执行,如果该事件没有发生,那么将自身挂起,因而处于等待或挂起态。
等待某事件而被挂起的任务,系统将会从OSRdyGrp和 OSRdyTbl[ ]中把该任务的优先级去掉(也就是相应位清0,因此在事件还没发生时,这个任务将不会再被调度),转而将该任务(优先级)添加在在该事件的等待列表中。
事件分为:信号量、互斥信号量,消息邮箱、队列消息。每一个信号量、互斥信号量,消息邮箱、消息队列都会分配到一个事件控制块ECB.
每个ECB都会有 事件的等待列表OSEvenGrp和 OSEvenTbl[ ] ,它与就绪表中OSRdyGrp和OSRdyTbl[ ]极为相似。等待某事件而被挂起的任务,系统会将该任务优先级写入事件的等待列表OSEvenGrp和 OSEvenTbl[ ]。
当某一事件发生了,那么该事件的ECB会从事件的等待列表选取优先级最高的任务获得事件,离开等待状态,从而进入就绪状态。
中断态(Interrupt):正在运行的任务可以被中断,除非是该任务将中断关闭。被中断的任务进入中断服务程序(ISR)。如果中断服务程序使一个更高优先级的任务准备就绪,这中断服务程序结束后,则更高优先级的任务开始运行程序。
我觉得uC/Osii工作,其实根本上就是控制多个任务在这5钟状态下转换,并且任务之间互相通信。所以一下从一个状态到另一个状态分析。