资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,SOURCE4,C,/OS-II,微小内核分析,绪论,C,/OS-II,微小内核是为了方便初学者学习嵌入式实时操作系统的基本原理,分别由小到大将,C,/OS-II V2.52,裁减为几个只具备基本功能的微小内核。,建议:初学者学习或者教师讲授本章内容时,一定要尽量做到一边阅读源代码一边画图,事实上“过程比结论更重要”!,SOURCE3,SOURCE2,SOURCE1,1100,行(剔除文件头和函数头后的数目),只有,418,行(剔除文件头和函数头后的数目),仅包含,5,个最基本的服务函数的“,最小内核,”。,最小内核,|,C/OS-II,微小内核分析,任务级的任务调度小结,通过对上述内容的学习,可以看出下图就是从,Task0,到,Task1,,然后再从,Task1,到,OS_TaskIdle,的切换过程。,任务级的任务调度,如果,ticks,非,0,,则将当前任务,Task0,从就绪表中删除,并将,ticks,延时节拍数保存到当前任务,Task0,的,OS_TCB,中,然后进行一次任务调度,并且执行下一个处于就绪态优先级最高的任务,Task1,。,ticks,非,0,,则将当前任务,Task1,从就绪表中删除,并将,ticks,延时节拍数保存到当前任务,Task1,的,OS_TCB,中,然后再进行一次任务调度,并且执行下一个处于就绪态优先级最高的任务,OS_TaskIdle,。,C,/OS-II,微小内核分析,临界区与中断管理,1,2,3,4,5,6,可重入性,案例分析,禁止,/,允许中断,时钟节拍,中断服务程序,中断管理,临界区与中断管理,|,C,/OS-II,微小内核分析,可重入性,可重入的代码指的是一段代码可以被多个任务同时调用,而不必担心数据被破坏。即就是说,可重入型函数在任何时候都可以被中断,一段时间以后又可以继续运行,而相应数据却不会丢失。,可重入型函数或者只使用局部变量,即变量保存在,CPU,寄存器或堆栈中;或者使用全局变量。当使用全局变量时,则要对全局变量予以保护。,可重入性代码,任务,1,任务,2,任务,3,代码的可重入性是保证完成多任务的基础。,调用,调用,调用,临界区与中断管理,|,C,/OS-II,微小内核分析,案例分析,在,RTOS,中,有可能几个任务需要访问同一个资源。同一个资源往往不能被几个任务同时访问,否则可能会破坏资源。全局变量是最简单的资源,本例用,2,个全局变量模拟,1,个共享资源,让低优先级任务依次改变,2,个全局变量的值,让它们的值始终相等。而高优先级的任务则定时检查这,2,个变量的值是否相等,如果不相等,则让,LED2,闪烁。,临界区与中断管理,|,C,/OS-II,微小内核分析,案例分析,void Task0(void *,pdata,),while(1),OS_ENTER_CRITICAL();,if(sum1!=sum2),if(i%2)=0),IO2CLR=LED2;,else,IO2SET=LED2;,i+;,OS_EXIT_CRITICAL();,初始化工作,允许中断,下面给出,Task0,任务的主要处理代码。,LED1,闪烁,关键代码,禁止中断,如果,sum1,与,sum2,不相等,则根,据,i%2,的值点亮或熄灭,LED2,临界区与中断管理,|,C,/OS-II,微小内核分析,案例分析,void Task1(void *,pdata,),pdata,=,pdata,;,while(1),OS_ENTER_CRITICAL();,sum1+;,sum2+;,OS_EXIT_CRITICAL();,防止编译器报警,sum1,和,sum2,加,1,下面给出,Task1,任务的主要处理代码。,允许中断,关键代码,禁止中断,由于,CPU,在执行关键代码时,中断被屏蔽,因此不可能执行中断服务程序,也就不会引起中断切换。此时,只要,Task0(),运行,,sum1,和,sum2,就相等,因此,,LED2,不会闪烁。,临界区与中断管理,|,C,/OS-II,微小内核分析,案例分析,void Task0(void *,pdata,),while(1),if(sum1!=sum2),初始化工作,当删除了禁止中断和允许中断的语句后,任务间可能出现以下情况。,sum1,与,sum2,相等,不执行括号内的代码,LED1,闪烁,void Task1(void *,pdata,),pdata,=,pdata,;,while(1),sum1+;,sum2+;,sum1,加,1,防止编译器报警,执行任务,1,任务调度,发生中断,任务调度,sum1,与,sum2,不相等,执行括号内的代码,LED2,闪烁,临界区与中断管理,|,C,/OS-II,微小内核分析,禁止,/,允许中断,和其它内核一样,,C,/OS-,为了处理临界区代码需要禁止中断,处理完毕后再允许中断,这使得,C,/OS-,能够避免同时有其它任务或中断服务进入临界段代码。,为了避开不同,C,编译器厂商选择不同的方法来处理禁止中断和允许中断,,,C,/OS-,定义两个宏来禁止中断和允许中断,分别是:,OS_ENTER_CRITICAL(),和,OS_EXIT_CRITICAL(),。,OS_ENTER_CRITICAL(),临界区代码,OS_EXIT_CRITICAL(),需成对出现,临界区与中断管理,|,C,/OS-II,微小内核分析,禁止,/,允许中断,在,ARM7,中,,OS_ENTER_CRITICAL(),和,OS_EXIT_CRITICAL(),是通过使用软件中断异常实现的,详细请参考,SWI,软件中断异常章节。,ENTER_CRITICAL,LDR R1,=OsEnterSum,LDRB R2,R1,ADD R2,R2,#1,STRB R2,R1,MRS R0,SPSR,ORR R0,R0,#NoInt,MSR SPSR_c,R0,MOVS PC,LR,禁止中断,关中断计数器,OsEnterSum,加,1,返回,OS_ENTER_CRITICAL,的实现代码如下:,临界区与中断管理,|,C,/OS-II,微小内核分析,禁止,/,允许中断,OS_EXIT_CRITICAL(),的实现代码如下:,EXIT_CRITICAL,LDR R1,=OsEnterSum,LDRB R2,R1,SUB R2,R2,#1,STRB R2,R1,CMP R2,#0,MRSEQ R0,SPSR,BICEQ R0,R0,#NoInt,MSREQ SPSR_c,R0,MOVS PC,LR,OsEnterSum,为,0,,则允许中断,关中断计数器,OsEnterSum,加,1,返回,判断,OsEnterSum,是否为,0,在,ARM,处理器核中禁止中断和禁止中断是通过改变程序状态寄存器,CPSR,中的相应控制位来实现的。由于使用了软件中断,程序状态寄存器,CPSR,保存到程序状态保存寄存器,SPSR,中,软件中断退出时会将,SPSR,恢复到,CPSR,中,所以程序只要改变程序状态保存寄存器,SPSR,中相应的控制位就可以了。,临界区与中断管理,|,C,/OS-II,微小内核分析,时钟节拍,时钟节拍是特定的周期性中断,,时钟节拍源一般是由专门的硬件定时器产生的,该定时器是一个周期性定时器,该定时器产生周期性的中断,这个中断可以看作是系统心脏的脉动。一般情况下,用户在第一个任务中开启时钟节拍器,,,以避免用户程序崩溃。,C,/OS-,中的时钟节拍服务是通过在中断服务程序中调用,OSTimeTick,(),实现的,,OSTimeTick,(),是为系统提供时钟节拍的服务程序。,函数名称,OSTimeTick,所属文件,OS_CORE.C,函数原型,void,OSTimeTick,(void),功能描述,时间节拍处理函数。减少任务的一个延时节拍数,并判断任务是否延时结束,如果延时结束,则让任务进入就绪状态,函数参数,无,函数返回值,无,特殊说明,由时钟节拍中断处理程序调用,用户很少使用,临界区与中断管理,|,C,/OS-II,微小内核分析,时钟节拍,使用,timer0,产生周期性时钟节拍,其过程如下图所示。,VIC,初始化,定时器,0,初始化,Timer0,中断服务程序,产生中断,调用,OSTimeTick,(),减少任务一个延时节拍数,,并判断任务是否延时结束。如果延时结束,则任务进入就绪状态。,其中,,VIC,初始化函数为,VICInit,(),,定时器,0,初始化函数为,imer0Init(),,,timer0,中断服务函数为,Timer0_Exception(),。,临界区与中断管理,|,C,/OS-II,微小内核分析,时钟节拍,VIC,初始化函数,VICInit,(),和定时器,0,初始化函数,Timer0Init(),如下。,#define OS_TICKS_PER_SEC 200,void VICInit(void),.,设置定时器,0,的中断优先级;,设置定时器,0,的中断服务程序入口;,使能定时器,0,中断;,其它中断初始化,设置中断优先级为,0,(最高),在,OS_CFG.h,中定义,一秒钟,200,个时钟节拍,设置,Timer0,的中断服务地址为,Timer0_Exception,void Timer0Init(void),T0IR =0 xff;,T0TC =0,;,T0TCR =0 x01,;,T0MCR=0 x03,;,T0MR0 =(Fpclk/OS_TICKS_PER_SEC),设置,TC,的计数值为,0,使能,Timer0,清除中断,TC,值匹配时产生中断,且,TC,值复位,设定匹配值,临界区与中断管理,|,C,/OS-II,微小内核分析,时钟节拍,void Timer0_Exception(void),T0IR =0 x01;,VICvectAddr,=0;,OSTimeTick,();,通知中断控制器中断结束,调用时钟节拍函数,清除,Timer0,中断,当完成,VIC,跟,Timer0,的初始化后,如果此时允许中断,则,CPU,跳转到中断异常入口处,同时,CPU,切换到,IRQ,中断模式,“中断返回地址,+4”,保存在,IRQ,中断模式下的,LR,寄存器中,再跳转到中断异常处理程序,IRQ_Handler,,获取,Timer0,中断服务程序地址,Timer0_Exception,。,C/OS-,中的函数,OSTimeTick(),在周期性中断的中断服务程序,Timer0_Exception(),中被调用,,,C/OS-,会在中断退出时调用另一个为中断编写的调度器,,,让处于就绪态优先级最高的任务运行。,临界区与中断管理,|,C,/OS-II,微小内核分析,中断服务程序,一般来说,,C,/OS-II,中,中断服务程序的全部或部分用汇编语言来写。当然,部分芯片的部分编译器可能可以全部使用,C,语言编写,但毕竟是少数,中断服务程序的图解如下。,执行用户代码(中断服务),保存全部寄存器,中断服务程序,调用,OSIntEnter,或,OSIntNesting,直接加,1,调用,OSIntExit,(),恢复所有,CPU,寄存器,执行中断返回指令,告诉,C,/OS-,此时正在执行中断服务,OSIntNesting,减,1,,如果,OSIntNesting,减到,0,时,则表明所有中断,包括嵌套的中断都已经完成。此时,C,/OS-,要判定有没有优先级更高的任务被中断服务程序唤醒,如果有优先级更高的任务进入了就绪态,那么,C,/OS-,就返回到那个更高优先级的任务。,临界区与中断管理,|,C,/OS-II,微小内核分析,中断服务程序,针对,ARM7,,当有,IRQ,中断产生且,CPU,允许相应中断时,,CPU,会跳转到,IRQ,中断异常入口处,(,异常向量表,),,同时,CPU,切换到,IRQ,中断模式,处理器会自动将“,IRQ,中断返回地址,+4”,保存到,IRQ,模式下的,LR,寄存器中,并将用户模式下的,CPSR,保存到,IRQ,模式下的,SPSR_irq,中。,“,IRQ,中断返回地址,+4”,保存到,IRQ,模式下的,LR,中,CPU,跳到,IRQ,中断异常入口,且,CPU,切换到,IRQ,中断模式,IRQ,中断,用户模式下的,CPSR,保存到,IRQ,模式下的,SPSR_irq,临界区与中断管理,|,C,/OS-II,微小内核分析,中断服务程序,在针对,ARM7,的,C,/OS-,中,移植代码提供了汇编接口代码,它完成了大部分必要的工作。对于,LPC,系列,ARM,,进入中断的汇编接口代码如下。,.,IRQ_Handler,SUB LR,LR,#4,STMFD SP!,R0-R3,R12,LR,MRS R3,SPSR,STMFD SP,R3,SP,LR,LDR R2,=OSIntNesting,LDRB R1,R2,ADD R1,R1,#1,STRB R1,R2,SUB SP,SP,#4*3,MSR CPSR_c,#(NoInt|SYS32Mode),CMP R1,#1,LDREQ SP,=StackUsr,BL$IRQ_Exception_Function,引入外部标号,计算,IRQ,中断返回地址,保存用户状态的,R3,SP,LR,注意不能回写,保存任务环境,OSIntNesting,+,调整,SP,指针内容,切换到系统模式,跳转到中断服务程序,如果当前中断是发生在任务执行时,则把,SP,设置为,StackUsr,如果不使用这两条指令,则实际的中断处理函数使用的是任务本身的堆栈,这样每个任务都需要为中断处理程序保留足够的堆栈,浪费了大量存储空间。当使用了这两条指令,那么任务就不需要为中断处理程序保留堆栈空间。对于,LPC2000,系列,ARM,,,StackUsr,一般就是,main(),函数的堆栈,与,main(),函数分时复用。由于调用,OSStart,(),之后才可能产生中断,而,OSStart,(),永远不会返回,main(),函数,此时,main(),函数的堆栈可以任意使用。,临界区与中断管理,|,C,/OS-II,微小内核分析,中断服务程序,退出中断的汇编接口代码如下。,MSR CPSR_c,#(NoInt|SYS32Mode),LDR R2,=OSEnterSum,MOV R1,#1,STR R1,R2,BL OSInitExit,LDR R2,=OSEnterSum,MOV R1,#0,STR R1,R2,MSR CPSR_c,#(NoInt|IRQ32Mode),LDMFD SP,R3,SP,LR,LDR R0,=OSTCBHighRdy,LDR R0,R0,LDR R1,=OSTCBCur,LDR R1,R1,CMP R0,R1,ADD SP,SP,#4*3,MSR SPSR_cxsf,R3,LDMEQFD SP!,R0-R3,R12,LR,LDR PC,=OSIntCtxSw,切换到系统模式,恢复用户状态的,R3,SP,LR,注意不能回写,调用,OSInitExit,调整,SP,指针内容,恢复,IRQ,中的,SPSR,根据,OSTCBHighRdy,与,OSTCBCur,的值决定是否进行任务切换,OsEnterSum,为,1,使,OSIntExit,退出时中断关闭,因为中断服务程序要退出,所以,OsEnterSum,=0,切换回到,IRQ,模式,比较,OSTCBHighRdy,指向的内容与,OSTCBCur,指向的内容,临界区与中断管理,|,C,/OS-II,微小内核分析,中断服务程序,SP,R12,LR,PC,CPSR,SPSR,SP,LR,R0,R1,R3,R4,R11,R2,SP,R12,LR,PC,CPSR,SPSR,SP,LR,R0,R1,R3,R4,R11,R2,退出中断时的出栈关系,进入中断时的压栈关系,寄存器,寄存器,用户模式,IRQ,模式,用户模式,IRQ,模式,0 x?,0 x?,0 x?,0 x?,0 x?,0 x?,0 x?,0 x?,0 x?,存储器,0 x?,1,2,3,R0,LR,SP,SPSR_irq,LR_irq,R12,R3,R2,R1,5,4,堆栈指针位置说明,入堆指针位置,出堆指针位置,进入和退出中断时的堆栈动作图示如下。,执行代码,STMFD SP,!,,R0-R3,,,R12,,,LR,保存任务环境;,执行代码,STMFD SP,,,R3,,,SP,,,LR,保存用户状态的,CPSR,;,执行代码,SUB SP,,,SP,,,#4*3,调整,SP,指针内容;,执行代码,LDMFD SP,,,R3,,,SP,,,LR,恢复用户状态的,R3,、,SP,、,LR,;,执行代码,ADD SP,,,SP,,,#4*3,调整,SP,指针内容;,执行代码,LDMEQFD SP,!,,R0-R3,,,R12,,,PC,恢复任务环境。,临界区与中断管理,|,C,/OS-II,微小内核分析,中断管理,由中断服务程序可知,,C,/OS-,的中断管理实际上就是,2,个函数,,OSIntEnter,(),和,OSIntExit,(),。,函数名称,OSIntEnter,所属文件,OS_CORE.C,函数原型,void,OSIntEnter,(void),功能描述,判断,RTOS,是否运行,当,RTOS,运行,且,OSIntNesting,小于,255,时,使,OSIntNesting,加,1,函数参数,无,函数返回值,无,特殊说明,进入中断时调用,函数名称,OSIntExit,所属文件,OS_CORE.C,函数原型,void,OSIntExit,(void),功能描述,判断,RTOS,是否运行,当,RTOS,运行,且,OSIntNesting,0,时,使,OSIntNesting,减,1,,此时如果,OSIntNesting,等于,0,,则查找进入就绪态且优先级最高的任务,再把这个任务的优先级与当前任务的优先级进行比较,不相等则进行调度,函数参数,无,函数返回值,无,特殊说明,退出中断时调用,临界区与中断管理,|,C,/OS-II,微小内核分析,中断管理,void,OSIntEnter,(void),if(,OSRunning,=TRUE),if(,OSIntNesting,0),OSIntNesting,-;,if(,OSInterNesting,=0),OSIntExitY,=,OSUnMapTblOSRdyGrp,;,OSPrioHighRdy,=(INT8U)(OSIntExitY,OSTCBPrev,=(OS_TCB*)0;,if(OSTCBList,!=(OS_TCB*)0),OSTCBList,-,OSTCBPrev,=,ptcb,;,新增的代码,用于建立反向链表,加快删除任务,TCB,初始化,任务的结束,|,C,/OS-II,微小内核分析,删除任务,通过调用函数,OSTaskDel,(),,可以让处于就绪态、运行态和等待态的任务回到睡眠态,也就是说任务被删除。删除一个任务,其实际上就是将该任务从任务控制块链表中删除,并将它归还给空任务控制块链表。,函数名称,OSTaskDel,所属文件,OS_TASK.C,函数原型,INT8U,OSTaskDel,(INT8U,prio,),功能描述,删除一个指定优先级的任务。被删除的任务将回到休眠状态,任务被删除后可以用函数,OSTaskCreate,(),重新建立,函数参数,prio,:指定要删除任务的优先级,如果为,OS_PRIO_SELF,则删除自身,函数返回值,OS_NO_ERR,:函数调用成功,OS_TASK_DEL_IDLE,:错误,试图删除空闲任务(,Idle task,),OS_TASK_DEL_ ERR,:错误,指定要删除的任务不存在,OS_PRIO_INVALID,:参数指定的优先级大于,OS_LOWEST_PRIO,OS_TASK_DEL_ISR,:错误,试图在中断处理程序中删除任务,任务的结束,|,C,/OS-II,微小内核分析,删除任务,函数,OSTaskDel,(),流程图如下所示。,OSTaskDel,(),先检查删除任务的各种条件是否成立,如果不成立,则返回相应的错误码。一旦所有条件都满足了,,OS_TCB,就会从所有可能的,C/OS-,的数据结构中移除。首先本任务从就绪表中删除,然后本任务从索引表数组,OSTCBPrioTbl,中删除,接着本任务从已使用的任务控制块链表中删除,最后将任务控制块加到空闲,TCB,链表中。这些都做完后,启动调度器运行下一个优先级最高的就绪任务,任务删除就结束了。,任务的结束,|,C,/OS-II,微小内核分析,删除任务,INT8U,OSTaskDel,(INT8U,prio,),OS_TCB *,ptcb,;,if(,OSIntNesting,0),return(OS_TASK_DEL_ISR);,#if OS_ARG_CHK_EN 0,if(,prio,=OS_IDLE_PRIO),return(OS_TASK_DEL_IDLE);,if(,prio,=OS_LOWEST_PRIO&,prio,!=OS_PRIO_SELF),return(OS_PRIO_INVALID);,#,endif,OS_ENTER_CRITICAL();,if(,prio,=OS_PRIO_SELF),prio,=,OSTCBCur,-,OSTCBPrio,;,ptcb,=,OSTCBPrioTblprio,;,if(,ptcb,!=(OS_TCB*)0),if(,OSRdyTblptcb,-OSTCBY&=,ptcb,-,OSTCBBitX,)=0 x00),OSRdyGrp,&=,ptcb,-,OSTCBBitY,;,ptcb,-,OSTCBDly,=0;,ptcb,-,OSTCBStat,=OS_STAT_RDY;,OSTaskDel,的代码如下。,不能在中断中删除任务,不能删除空闲任务,任务优先级非法,将任务从就绪表中删除,获得当前任务优先级,获得要删除任务的,TCB,任务的结束,|,C,/OS-II,微小内核分析,删除任务,OSTCBPrioTblprio,=(OS_TCB*)0;,if(,ptcb,-,OSTCBPrev,=(OS_TCB*)0),ptcb,-,OSTCBNext,-,OSTCBPrev,=(OS_TCB*)0;,OSTCBList,=,ptcb,-,OSTCBNext,;,else,ptcb,-,OSTCBPrev,-,OSTCBNext,=,ptcb,-,OSTCBNext,;,ptcb,-,OSTCBNext,-,OSTCBPrev,=,ptcb,-,OSTCBPrev,;,ptcb,-,OSTCBNext,=,OSTCBFreeList,;,OSTCBFreeList,=,ptcb,;,OS_EXIT_CRITICAL();,OS_Sched,();,return(OS_NO_ERR);,OS_EXIT_CRITICAL();,return(OS_TASK_DEL_ERR);,在索引表中删除本任务,从已使用的任务控制块链,表中删除对应的任务控制,块,加到空闲任务控制块中,任务调度,C,/OS-II,微小内核分析,信号量,1,2,3,4,5,事件与信号量,事件控制块,改进的任务控制块及任务删除函数,改进的,OS,初始化,信号量管理,6,删除信号量,信号量,|,C,/OS-II,微小内核分析,事件与信号量,在嵌入式实时多任务系统中,为了使系统达到高效处理和快速响应的目的,于是大量采用“事件驱动”的方式来编写任务。而事件可能是外部的,比如外部设备中断;也可能是嵌入式系统内部产生的,比如一个任务给另一个任务发送信号量或消息。由此可见,用于任务同步和通信的信号量、消息邮箱、消息队列和互斥信号量都叫做“事件”。,事件,信号量,(,Sem,),消息邮箱,(,Mbox,),消息队列,(Q),互斥信号量,(,Mutex,),信号量,|,C,/OS-II,微小内核分析,事件与信号量,我们知道酒店的桌子数是固定的,因此可以这样理解其最大桌子数就是计数器的初值,假设一人占用一张桌子,因此每进去一人,计数器就会自动减,1,,而只有出去一人时计数器才会自动加,1,。即就是说,如果计数器大于,0,,就可以进去吃饭,否则只好等待有人出来才能进去,这种计数信号就是信号量。,酒店,人,计数器,(能容纳的人数),3,2,1,0,人,人,人,禁止其它客人进入,人,没有空位,不能再容纳客人,有空位,能再容纳,1,位客人,信号量,|,C,/OS-II,微小内核分析,事件与信号量,信号量就像通行证,且通行证的数目是有限的,任务要运行下去就必须先拿到通行证。如果信号量已被别的任务占用,那么该任务只能被挂起,直到信号量被当前使用者释放为止。,信号量的值可以是,0,到,255,、,0,到,65535,或,0,到,4294967295,,取决于信号量规约机制使用的是,8,位、,16,位还是,32,位。到底是几位,实际上取决于用的是哪种内核。对于,C,/OS-II,来说,信号量使用,16,位,其取值范围为,0,65535,。一般地说,对信号量只能实施,3,种操作:,建立信号量,(,初始化,),、等待信号量,(,挂起,),、发送信号量。注意:信号量初始化时一定要给信号量赋初值,并将等待信号量任务列表清空。,操作信号量,建立信号量,等待信号量,发送信号量,OSSemPend,(),OSSemCreate,(),OSSemPost,(),信号量,|,C,/OS-II,微小内核分析,事件控制块,C/OS-II,将信号量、互斥信号量、消息邮箱、消息队列等统称为“,事件,”,然后通过一个称为“事件控制块(,ECB,)”的数据结构来管理事件,也就是说,任务和中断服务程序可以通过,ECB,向另外的任务程序发送信号,任务也可以等待另一个任务或者中断服务程序给它发送信号。事件控制块的结构如下图所示。,信号量的计数器或互斥信号量和优先级继承的计数器,事件类型,指向邮箱或消息队列的指针,等待任务列表,信号量,|,C,/OS-II,微小内核分析,事件控制块,事件控制块的定义如下所示。,typedef,struct,INT8U,OSEventType,;,INT16U,OSEventCnt,;,void *,OSEventPtr,;,INT8U,OSEventGrp,;,INT8U,OSEventTblOS_EVENT_TBL_SIZE,;,OS_EVENT;,OSEventType,定义了事件的具体类型。在,C/OS-II,微小内核中只能为,OS_EVENT_SEM,(信号量)和,OS_EVENT_TYPE_UNUSED,(未分配)。,当然,在完整版的,C/OS-II,还可以是,OS_EVENT_TYPE_MBOX,(消息邮箱)、,OS_EVENT_TYPE_Q,(消息队列)和,OS_EVENT_TYPE_MUTEX,(互斥信号量)。,当事件控制块用于信号量时,,OSEventCnt,用作信号量的计数器;当事件控制块用于互斥信号量时,互斥信号量和优先级继承优先级的计数器。,OSEventPtr,指针只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息;而当所定义的事件是消息队列时,它指向消息队列控制块。当然,空闲事件控制块链表也用它做链表指针。,它们共同构成事件控制块的等待任务列表。,信号量,|,C,/OS-II,微小内核分析,事件控制块,对于等待事件任务的记录,COS,-II,采用了与任务就绪表类似的方法,使用一个,INT8U,类型的数组,OSEventTbl,作为记录等待事件任务的记录表,这个表叫等待任务表,如下图所示。,索引表,非必须的,为加快处理速度而引入,根据任务的优先级可以确定该任务在等待任务列表中的确切位置,真正的等待任务列表,OSEventTbl,虽然被定义为“字节数组”,但仍被看作是“位数组”,即每一位代表一个任务,其中任何一位为,1,表示对应的任务为事件的等待任务,否则不是等待任务。等待任务表,OSEventTbl,数组的大小取决于,OS_LOWEST_PRIO,的值,确定事件的等待任务在,OSEventTblOS_LOWEST_PRIO/8+1,中的位置,即横坐标,X,确定变量,OSEventGrp,具体数据位,用于确定等待任务表数组元素的下标,即纵坐标,Y,信号量,|,C,/OS-II,微小内核分析,事件控制块,下面将给出一个任务放到事件的等待任务列表中的代码和从等待任务列表中删除一个任务的代码,需要说明的是,,OS_EVENT_TBL_SIZE,被定义为,(OS_LOWEST_PRIO)/8+1),。,1,、将任务加入等待任务列表,2,、从等待任务列表中删除任务,pevent,-,OSEventGrp,|=,OSMapTblprio,3;,pevent,-,OSEventTblprio,3|=,OSMapTblprio,if(,pevent,-,OSEventTblprio,3&=,OSMapTblprio,&0 x07)=0),pevent,-,OSEventGrp,&=,OSMapTblprio,3;,信号量,|,C,/OS-II,微小内核分析,事件控制块,INT8U const,OSUnMapTbl,=,/0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x00 to 0 x0F*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x10 to 0 x1F*/,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x20 to 0 x2F*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x30 to 0 x3F*/,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x40 to 0 x4F*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x50 to 0 x5F*/,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x60 to 0 x6F*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x70 to 0 x7F*/,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x80 to 0 x8F*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 x90 to 0 x9F*/,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 xA0 to 0 xAF*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 xB0 to 0 xBF*/,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 xC0 to 0 xCF*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 xD0 to 0 xDF*/,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0 xE0 to 0 xEF*/,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0/*0 xF0 to 0 xFF*/,;,若,OSRdyGrp,为,0 x68,,以此值为偏移量查表,得,y,值为,3,若,OSRdyTb1y,为,0 x30,,以此值为偏移量表,得,x,值为,2,就绪任务组优先级最高的就是,26,(,3,OSEventGrp,;,x =,OSUnMapTblpevent,-,OSEventTbly,;,prio,=(y 0)&(OS_MAX_EVENTS 0),#if(OS_MAX_EVENTS 1),INT16U i;,OS_EVENT *pevent1;,OS_EVENT *pevent2;,pevent1=,pevent2=,for(i=0;i,OSEventType,=OS_EVENT_TYPE_UNUSED;,pevent1-,OSEventPtr,=pevent2;,pevent1+;,pevent2+;,pevent1-,OSEventType,=OS_EVENT_TYPE_UNUSED;,pevent1-,OSEventPtr,=(OS_EVENT*)0;,OSEventFreeList,=,#else,OSEventFreeList,=,OSEventFreeList,-,OSEventType,=OS_EVENT_TYPE_UNUSED;,OSEventFreeList,-,OSEventPtr,=(OS_EVENT*)0,;,#,endif,#,endif,空闲事件控制块链表初始化代码如下。,当有多个事件时,执行下,面代码,把所有空闲事件控制块,的类型设置为“未分配”,链表尾指针指向“,NULL”,链表头指针指向第一个空闲任务块,只有一个任务控制块时的处理程序,信号量,|,C,/OS-II,微小内核分析,事件控制块,使等待超时的任务进入就绪态,使任务进入等待状态,初始化事件控制块,使等待事件的任务进入就绪态,事件控制块,1,3,2,4,对于事件控制块有一些通用的操作,如下图所示。,为了避免代码重复和减短程序代码长度,,C/OS-II,将上面的操作用,4,个系统函数来实现,它们分别是:,OS_EventWaitListInit,(),,,OS_EventTaskRdy,(),,,OS_EventTaskWait,(),和,OS_EventTO,(),,下面将分别逐一介绍。,信号量,|,C,/OS-II,微小内核分析,事件控制块,1,、初始化事件控制块,当建立一个信号量、互斥信号量、消息邮箱或消息队列时,需要对事件控制块中的等待任务列表进行初始化,这是通过调用函数,OS_EventWaitListInit,(),来实现的。,函数名称,OS_EventWaitListInit,所属文件,OS_CORE.C,函数原型,void,OS_EventWaitListInit,(OS_EVENT*,pevent,),功能描述,初始化一个空的等待任务列表,使其中没有任何任务,函数参数,pevent,:指向需要初始化的事件控制块,函数返回值,无,特殊说明,在创建事件时被调用,由上面的分析可知,让等待任务列表没有任何任务就是将事件控制块,(ECB),的,OSEventGrp,成员和,OSEventTbl,的所有成员赋值为,0,。,信号量,|,C,/OS-II,微小内核分析,事件控制块,void,OS_EventWaitListInit,(OS_EVENT*,pevent,),INT8U *,ptbl,;,pevent,-,OSEventGrp,=0 x00;,ptbl,=&,pevent,-OSEventTbl0;,#if OS_EVENT_TBL_SIZE 0,*,ptbl,+=0 x00;,#,endif,OS_EventWaitListInit,的源代码如下。,将,OSEventGrp,赋值为,0,将,OSEventTbl,的所有成员赋值为,0,信号量,|,C,/OS-II,微小内核分析,事件控制块,函数名称,OS_EventTaskWait,所属文件,OS_CORE.C,函数原型,void,OS_EventTaskWait,(OS_EVENT*,pevent,),功能描述,将任务从就绪任务表中删除,并放到相应事件的事件控制块的等待任务表中,函数参数,pevent,:指向需要进入等待状态的任务的事件控制块,函数返回值,无,特殊说明,在请求一个事件时被调用,2,、使任务进入等待状态,当某个任务要等待一个事件的发生时,需要将任务从就绪任务表中删除,并放到相应事件的事件控制块的等待任务表中,这些操作是通过调用函数,OS_Eve
展开阅读全文