Nucleus实时操作系统分析报告

上传人:小**** 文档编号:69126816 上传时间:2022-04-05 格式:DOC 页数:35 大小:405.50KB
收藏 版权申诉 举报 下载
Nucleus实时操作系统分析报告_第1页
第1页 / 共35页
Nucleus实时操作系统分析报告_第2页
第2页 / 共35页
Nucleus实时操作系统分析报告_第3页
第3页 / 共35页
资源描述:

《Nucleus实时操作系统分析报告》由会员分享,可在线阅读,更多相关《Nucleus实时操作系统分析报告(35页珍藏版)》请在装配图网上搜索。

1、Nucleus分析报告Nucleus实时操作系统分析报告目 录一、NUCLEUS的内核(KERNEL)31.1系统启动31.2初始化线程41.3线程调度41.3.1 任务的调度51.3.2 中断的调度91.3.3 操作系统数据结构的保护141.4任务间通信161.4.1 消息管道(Pipes)171.5任务的同步211.6定时器211.7内存管理221.8输入/输出设备驱动23Nucleus内核总结:23二、NUCLEUS的开发工具242.1Nucleus C+242.2Nucleus MNT242.3Nucleus VNET242.4Nucleus PC+242.5Nucleus FILE2

2、42.6Nucleus CLIB252.7Nucleus GRAFIX252.8Nucleus EDE252.9Nucleus UDB262.10Nucleus DEBUG+262.11第三方产品的支持282.12SDS SingleStep28三、NUCLEUS的网络支持303.1Internet套件30Nucleus NET30Nucleus SNMP30Nucleus RMON31Nucleus SPAN313.2Nucleus WebServ313.3Nucleus Jvi313.4Nucleus EPILOGUE31Nucleus实时操作系统分析报告Nucleus实时操作系统是Acc

3、elerated Technology公司开发的嵌入式RTOS产品,只需一次性购买Licenses,就可以获得操作系统的源码。Nucleus购买的灵活性比较大:Kernel,Networking,File System,Web Technology,Target Debugger可以分开购买,如果我们只需要微内核的话只要购买Kernal和Debugger,当前的下位机就是这样配置的。但是,如果以后我们要开发接入服务器和IP Phone的话就必须购买一堆网络协议;上位机要用的话还要购买文件系统。Nucleus的另一大好处是程序员不用写板支持软件包(BSP),因为操作系统已经开放给程序员,不同的目

4、标板在操作系统BOOT时可以通过修改源码进行不同的配置。对于程序员来说,写BSP是一项比较繁琐的任务,有了OS的源码这项工作就简单多了,同时调试时也可以跟踪到中断、寄存器那一级,简化了硬件的调试。VxWorks和pSOS都必须购买标准的BSP模板工具来写板支持软件包,开发工作量就比较大了。Nucleus对CPU的支持能力比较强,支持当前流行的大多数RISC、CISC、DSP处理器,比如:80x86(实时、保护模式)、68xxx、PowerPC、i960、MIPS、SH、ARM、ColdFire。Nucleus系统对于不同的处理器,操作系统的源码大部分是相同的,只有5%的源码是用汇编写的跟CPU

5、有关。如果使用另外的CPU,则只用修改5、6个汇编程序就可以进行移植,但是对于不同系列的处理器,它们的Compiler、Linker、Debugger是不同的(要另外购买),而且编译预处理的过程也是不一样的,这就要投入一部分力量去进行操作系统的移植。当然,这也是Nucleus的一个优点,VxWorks和pSOS都没有源码,换一个系列的CPU操作系统又要重新购买,实际上他们OS开发的工作量并没有多少,但却造成了用户的重复性投资。一、 Nucleus的内核(Kernel)Nucleus的核心是一个实时的多任务内核Nucleus PLUS,具有以下特性(Accelerated Technology公

6、司宣称的):可移植性: Nucleus PLUS可用于大多数流行的处理器。因为Nucleus PLUS主要是用标准C写的,移植到新的处理器系列相对很直接。这也就意味着用Nucleus PLUS开发的应用程序也具有很高的可移植性。可用性: 不像其他的商业内核,Nucleus PLUS的系统调用名直接表明了它的功能。比如,你可以通过Nu_Create_Task系统调用来创建一个任务。Nucleus PLUS的系统调用都设计成具有类似的入口参数和返回值类型。Nucleus PLUS的对象都不隐式地和别的对象相关。比如,邮箱和任务之间是不相关的。因此,用户可以利用多个Nucleus PLUS对象之间的

7、结合形成混合系统调用。配置: Nucleus PLUS最终是以C库的形式提供给用户,你可以选择所需要的部分链入到你的应用程序中。特性:、快速响应时间:对临界资源的检测时间不依赖于占有该临界资源的线程执行时间的长短,一旦低优先级线程释放掉临界资源(不管其是否执行完),高优先级线程就会抢占运行。、每个任务的执行时间和其他任务的处理时间无关。、较高吞吐量:随着任务数目的增多,任务的调度时间为常数。、可扩展性:利用现有系统调用的结合可得到新的系统调用。Nucleus PLUS提供其他实时内核都具有的系统服务,比如:任务控制、任务通信、任务同步、内存管理、可编程的定时器、标准的输入/输出设备接口等。对任

8、务的调度依赖优先级、时间片的方法,可以选择挂起一个任务按照先入先出(FIFO)的顺序或按照优先级的顺序。所有的操作系统对象(任务、邮箱、队列、管道等)都可以动态地创建和删除。创建一个对象时,要指定指定其控制块的内存区域和其它的数据要求(堆栈空间等)。Nucleus在任务调度时,首先查看任务是否可以抢占,如果不能抢占,则一直执行到任务完成或任务放弃时间片;否则,依靠优先级进行调度,先调入优先级最高的任务,对于优先级相同的任务则分享时间片、轮流调度。/ben:请问这里的时间片是系统节拍还是由多个节拍组成的时间片,猜测应该就是时间节拍若每个任务分配一些时间片,怎样保证实时?/错了,1.1 系统启动对

9、于68K系列的CPU,如果采用CrossCode C编译器的话,Nucleus使用的是CrossCode C的启动函数,标号START是系统的入口点。START标号在文件start.s中,用汇编及CrossCode C的宏指令写的。主要完成68360中断向量表指针VBR和寄存器基址MBAR的初始化,以及外部RAM各分区的初始化,这些分区包括:ram,data,malloc,init,sys_memory等。系统低级初始化完成后,控制就交给初始化线程INT_Initialize。1.2 初始化线程初始化线程是系统开始执行的第一个线程,线程的入口是INT_Initialize,同时也是系统的主函数

10、,具有另外的标号名main。系统初始化首先完成硬件寄存器的配置,包括:存储器片选(CS0CS7),软件看门狗SWT,系统周期定时器PIT,A口、B口、C口引脚功能设定,串行通信控制器的初步配置等。这些硬件的配置跟目标板有关,需要用户自己用汇编来写。其次,将系统堆栈指针TCD_System_Stack初始化为堆栈区stack的顶部,同时在系统内存区sys_memory中拿出TMD_HISR_Stack_Size大小的一片内存用作高级中断服务程序的堆栈HISR_STACK。 然后,控制交给INC_Initialize。INC_Initialize首先完成操作系统数据结构的初始化,包括:线程控制、邮

11、箱、队列、管道、信号量、事件、分区内存、动态内存、定时器、I/O Driver等。其次,调用Application_Initialize,这一函数由用户编写,完成任务、消息队列等的创建、中断的注册以及应用程序的初始化。当所有的初始化都完成后,INC_Initialize调用TCT_Schedule开始线程的调度。1.3 线程调度TCT_Schedule是线程调度的入口,负责将控制权交给具有最高优先级的高级中断服务程序HISR(TCD_Execute_HISR)或处于就绪状态的最高优先级任务(TCD_Execute_Task)。当没有任务或HISR执行时,线程调度就在TCT_Schedule中做

12、死循环,等待TASK或HISR就绪。HISR的优先权比任务高,一旦有HISR就绪,则当前调度的任务将会被挂起,优先调度HISR。一旦有TASK或HISR就绪,控制就会交给TCT_Control_To_Thread,在这里将TCD_Execute_HISR或TCD_Execute_Task设置为当前线程TCD_Current_Thread,启动任务时间片定时器,根据线程的不同堆栈类型恢复堆栈,然后执行RTS或RTE指令,将控制权交给线程。Nucleus的线程有两种类型的堆栈帧:/ben:有什么区别呢?任务创建时,要建立一个初始堆栈帧,线程入口是TCC_Task_Shell,该Shell执行任务的

13、入口程序,通常任务的执行是一个死循环,不停地在等待消息或事件,如果没有消息或事件任务就会挂起,否则往下执行。如果,任务在Shell调度中返回,则表示该任务已执行完毕,将任务终止,其状态置为NU_FINISHED。HISR创建时,也要建立一个初始堆栈帧,线程入口是TCC_HISR_Shell。HISR_Shell调度的是当前具有最高优先级的HISR,直至TCD_Execute_HISR的激活计数器tc_activation_count为0,才调度同一优先级或低优先级的其它HISR。HISR_Shell对HISR调度就是执行HISR的入口程序,然后根据激活计数器循环调度,HISR是不能被挂起的。1

14、.3.1 任务的调度 任务的调度需要用到以下比较重要的数据结构: TCD_Created_Tasks_List:已创建任务链表的头指针。 TCD_Priority_List256:任务控制块TCB的指针数组,每个元素是优先级0255就绪任务链表的头指针。 TCD_Priority_Groups:按位来定义的长字,对应32组优先级,每bit是一组,负责8个优先级,如果其中任意一个优先级有任务就绪,则该bit置1。 TCD_Sub_Priority_Groups32:子优先级组的位映像图,每一元素对应一组优先级。比如TCD_Sub_Priority_Groups0对应优先级07,bit0bit7分

15、别表示优先级07。 TCD_Highest_Priority:当前就绪任务的最高优先级,并不表示当前正在运行任务的优先级,如果该任务不能抢占。 TCD_Execute_Task:当前正在执行的任务指针。TCD_Current_Thread:当前正在执行的线程指针(TASK or HISR)。Nucleus的任务具有五种状态:executing,ready, suspended,finished,terminated。Executing:任务正在执行,但是其任务控制块TCB中的状态tc_status仍为NU_READY,只是该任务的指针等于当前线程TCD_Current_Thread。Ready

16、:任务已就绪,但是有其它任务在运行。Suspended:任务在等待请求服务完成的过程中被挂起,一旦请求服务完成,任务就会迁移至ready状态。Finished:该任务的处理已经完成(在TCC_Task_Shell调度中返回)。一旦任务处于这种状态就不能再执行了,除非任务被复位。Terminated:任务被Killed。一旦任务处于这种状态就不能再执行了,除非任务被复位。 Nucleus的每个任务都具有一个0255的优先级,0表示最高优先级,255表示最低优先级,对于相同优先级的任务分时间片运行,不同优先级的任务则可以发生抢占。 任务的调度涉及到:创建任务、删除任务、复位任务、终止任务、恢复任务

17、、挂起任务等。、创建任务(TCC_Create_Task)任务的创建一般是在Application_Initialize中进行,当然也可以在其它任务中动态地创建和删除任务。任务创建的流程如下:、删除任务(TCC_Delete_Task)对任务删除时,默认任务是处于finished或terminated状态。将任务删除,主要是将任务从已创建任务链表中删除,并不能释放与任务相关连的控制块(TCB)和堆栈。、复位任务(TCC_Reset_Task)当任务处于finished或terminated状态时,才能对该任务执行复位,否则返回NU_NOT_TERMINATED,表示任务没有结束或终止。任务复位

18、主要是将任务控制块中的数据成员重新赋初值,重新创建任务堆栈(任务堆栈复位),将任务状态置为无条件挂起状态NU_PURE_SUSPEND。、终止任务(TCC_Terminate_Task)如果要终止的是当前任务(TCD_Current_Thread),则直接将任务挂起,将任务状态置为NU_TERMINATED。如果要终止的不是当前任务,则要对任务状态进行判断。如果任务已经处于finished或terminated状态,则什么也不做。如果任务处于ready状态,则直接将任务挂起,将任务状态置为NU_TERMINATED。如果任务处于suspended状态,则必须释放和该任务相关的所有保护结构后才能

19、将任务终止。、恢复任务(TCC_Resume_Task)如果任务可以获得执行所需要的系统资源,比如:对于做NU_Send_To_Pipe系统调用的任务如果消息管道已有空余空间,或者做NU_Reveive_From_Pipe系统调用的任务如果管道中有消息,那么挂起在该管道上的任务就会恢复。如果任务挂起类型与请求恢复类型一致,则把任务状态置为NU_READY,将任务插入就绪任务优先级链表TCD_Priority_Listtask-tc_priority,设置优先级组TCD_Priority_Groups和子优先级组TCD_Sub_Priority_Groupstask-tc_priority/8中

20、本任务优先级对应的bit,指示本优先级有任务ready。如果要恢复的任务优先级比当前最高优先级TCD_Highest_Priority要高,且当前任务TCD_Execute_Task可以抢占,则将要恢复的任务置为当前任务,同时会产生任务抢占,返回NU_TRUE,否则返回NU_FALSE。、挂起任务(TCC_Suspend_Task)如果任务不能获得执行所需要的系统资源,比如:对于做NU_Send_To_Pipe系统调用的任务如果消息管道已满,或者做NU_Reveive_From_Pipe系统调用的任务如果管道已空,那么任务就会被挂起。任务挂起首先判断要挂起的是不是当前任务TCD_Current

21、_Thread,如果不是挂起当前任务,则要释放任务的当前保护结构tc_current_protect。其次,如果任务的状态为NU_READY且该任务优先级就绪任务链表TCD_Priority_List中只有这一个任务ready,则要清空该优先级就绪任务链表,同时要清除子优先级组TCD_Sub_Priority_Groups和优先级组TCD_Priority_Groups对应的bit。如果要挂起的任务具有最高优先级,则要根据优先组和子优先组重新搜索最高优先级,如果其他组中没有任务就绪,则TCD_Highest_Priority=255。然后根据最高优先级,重新调整TCD_Execute_Task

22、,如果最高优先级为255,则TCD_Execute_Task=NU_NULL。如果任务的状态为NU_READY且该任务优先级就绪任务链表TCD_Priority_List中不只这一个任务ready,则将该任务从优先级就绪任务链表中删除,不用修改子优先级组和优先级组,另外也不用调整最高优先级TCD_Highest_Priority,只是利用最高优先级重新调整TCD_Execute_Task。如果要挂起的是当前线程TCD_Current_Thread,则将控制交给TCD_Control_To_System,在TCD_Control_To_System中给当前线程创建一个solicited类型的堆栈

23、帧,线程入口是调用TCD_Control_To_System的下一条指令,任务恢复时从这条指令开始继续执行。TCD_Control_To_System随后又将控制交给TCT_Schedule,TCT_Schedule根据TCD_Execute_HISR或TCD_Execute_Task开始下一轮的任务调度,如果调度过程中发现挂起任务需要的系统资源可以满足,就会把任务恢复,按照优先级重新调度。任务挂起流程如下:1.3.2 中断的调度Nucleus的中断分为管理的和非管理的中断。管理的中断需要向操作系统注册该中断向量,中断产生后通过该中断向量注册的低级中断服务程序(LISR)来激活高级的中断服务程

24、序(HISR)。LISR主要完成硬件中断的处理,及激活HISR。HISR的调度类似于任务,具有优先级,可以使用大多数Nucleus的系统调用。非管理的中断,则不需要通过操作系统进行管理,直接将中断服务程序挂到中断向量表上,上下文的保存与恢复都要用户自己来做,该中断自己不能嵌套,最好不要被管理的中断再次中断否则会引起堆栈出错,而且非管理的中断不能使用绝大多数的Nucleus系统调用,因为它可能会破坏操作系统某些保护的数据结构(当有线程在运行时)。非管理的中断适用于那些比较频繁的中断,如果通过操作系统来管理这些中断的话,其上下文保存与恢复的时间就比较长,中断的实时性就不能满足要求。非管理的中断比较

25、简单,类似于以前我们写的中断服务程序,这里就不多说。下面我们讨论的中断的调度都指的是Nuclesu管理中断的调度。中断的调度需要用到以下比较重要的数据结构:TCD_Registered_LISRs256:对应68360的256个中断向量。0表示该中断向量没有注册,是操作系统不能处理的中断(Unhandled_Interrupt);非0表示该中断向量已注册,且其值为在LISR函数指针数组TCD_LISR_Pointers中的索引下标。TCD_LISR_Pointers30:LISR函数指针数组,指向当中断产生时要调用的低级中断服务程序LISR的入口函数。TCD_Interrupt_Count:表

26、示有多少个中断服务程序(ISRs)正在进行处理。0:没有中断;1:只有一个中断;1:中断嵌套。TCD_Interrupt_Level:允许中断的级别,用来给68360的状态寄存器SR赋值。0x700表示屏蔽所有中断,0x500表示屏蔽5级及5级以下的中断,0表示打开所有中断。TCD_Unhandled_Interrupt:系统出错时,表示不能处理的中断向量号。TCD_Created_HISRs_List:已创建的HISR链表的头指针。TCD_Active_HISR_Heads3:对应HISR优先级02,每个数组元素是该优先级已激活HISR链表的头指针。TCD_Active_HISR_Tails

27、3:对应HISR优先级02,每个数组元素是该优先级已激活HISR链表的尾指针。TCD_Execute_HISR:当前正在执行或要执行的具有最高优先级的HISR指针。每个HISR具有一个02的优先级,0表示最高优先级,2表示最低优先级,相同优先级的HISR按照先入先出的顺序处理,优先级不同的HISR按照优先级的高低进行调度。HISR是不能被挂起的,因此其所有的系统调用都要加上NU_NO_SUSPEND参数。 中断的调度包括:中断向量的注册、HISR的创建与删除、上下文的保护与恢复、LISR的执行、HISR的激活以及HISR的调度等。 、中断向量的注册(TCC_Register_LISR)一个中断

28、要通过操作系统管理起来,首先要将其中断向量通过系统调用NU_Register_LISR(INT vector, VOID (*new_lisr)(INT), VOID (*old_lisr)(INT)注册起来。该系统调用的第2个参数是LISR的入口函数指针,也就是中断产生后要执行的LISR。中断的注册首先要判断该中断向量是否已经注册过。如果该中断已经注册过,则利用TCD_Registered_LISRs索引到该中断向量在LISR函数指针数组TCD_LISR_Pointers中的下标,然后将新的LISR(new_lisr)填入TCD_LISR_Pointers。如果该中断向量没有注册过,则在LI

29、SR函数指针数组TCD_LISR_Pointers中找出一个没有使用的单元,将new_lisr填入该单元,同时将该单元的下标填入该中断向量在TCD_Registered_LISRs中对应的单元。另外,还要判断INT_Loaded_Flag标志,如果该标志为0,则要替换掉当前的中断向量表,否则不修改当前的中断向量表。、HISR的创建(TCC_Create_HISR)HISR也就是中断产生后,要在LISR中激活的高级中断服务程序。HISR的创建比任务创建简单,不用进行设置任务状态、恢复任务等操作,只需创建一个HISR控制块HCB,初始化HCB中的一些参数,为HISR创建一个Solicited类型的

30、堆栈帧,将该HISR的指针挂到已创建HISR链表TCD_Created_HISRs_List,同时分配一个用户指定的入口函数指针,该函数用来完成真正的中断处理。、HISR的删除(TCC_Delete_HISR) HISR的删除默认HISR处于非激活状态,仅仅是将HISR从已创建HISR链表TCD_Created_HISRs_List中删除,并清除HISR ID标志,并不能释放与HISR相关的内存(控制块、堆栈等),同时也不影响HISR的激活,对HISR的调度可能会产生微小的影响(由于HISR ID被清除)。一般来说,HISR的删除没有什么意义,除非把跟HISR相关的中断也关掉。、上下文的保护(

31、TCT_Interrupt_Context_Save)通常的中断服务程序对要用到的寄存器都要进行堆栈保护,Nucleus操作系统除了做这些外,还要对当前线程进行保护,使得高级中断服务程序HISR可以抢占任务,让HISR得到快速的响应。Nucleus在系统空闲(没有线程运行)时,中断堆栈使用的是系统堆栈TCD_System_Stack;如果有线程(任务或HISR)在运行,使用的是任务或HISR堆栈,上下文保护完成之后,则将堆栈切换到系统堆栈TCD_System_Stack。上下文保护首先将所有中断屏蔽掉,等保护完成之后再将中断打开。其次判断中断计数器TCD_Interrupt_Count,如果本

32、次中断是中断嵌套,则将TCD_Interrupt_Count加1,然后返回。如果本次中断不是中断嵌套,则判断当前有没有线程在运行,如果当前有线程在运行,则为当前线程建立一个Interrupt类型的堆栈帧,将当前的堆栈指针保存在线程控制块中,再将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回;如果当前没有线程运行,则直接将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回。、LISR的执行(TCC_Dispatch_LISR)中断上下文保护完成后,就要根据中断向量,在LISR指针数组TCD_LISR_Pointers中索引到本中断向量的LISR入口函数指针

33、,然后执行LISR函数,通常LISR要做的只是处理硬件中断及激活HISR。LISR执行完毕,则将上下文恢复,将控制权交给TCT_ Shedule进行系统调度。要注意的是,如果某个中断向量没有注册,则会产生系统错误,进入系统错误线程ERC_System_Error处理,这种错误是致命的错误,导致整个系统进入一个死循环。、上下文的恢复(TCT_Interrupt_Context_Restore)上下文恢复首先判断是不是中断嵌套,如果是中断嵌套,则将TCD_Interrupt_Count减1,将堆栈保护的寄存器恢复,然后利用RTE指令从中断返回。如果不是中断嵌套,则将当前线程TCD_Current_

34、Thread清为0,将堆栈切换到系统堆栈顶部TCD_System_Stack,将控制交给TCT_Shedule进行线程的重新调度,在这里HISR会抢占任务优先运行。对于没有嵌套的中断恢复,并没有执行RTE指令,从中断产生的指令往下执行,而是将控制交给TCT_Shedule进行重新调度,这类中断恢复可以分三种情况进行分析:i)、系统空闲(做TCD_Shedule死循环)时,产生了中断,则上下文恢复后,再次运行TCD_ Shedule,然后调度由LISR激活的HISR(TCD_Execute_HISR)。ii)、中断产生时,有一个任务在运行。由于在上下文保护时,已经给当前任务建立了一个中断类型的堆

35、栈帧,同时LISR运行时没有修改当前任务的状态(没有将当前任务挂起),也没有修改TCD_Execute_Task。当中断恢复完成之后,TCD_Shedule首先把TCD_Execute_HISR设置为当前线程TCD_Current_Thread,优先调度HISR,如果HISR运行过程中激活了一个比当前被中断任务优先级更高的任务,则TCD_Execute_Task会被修改,等HISR运行完毕,则将TCD_Execute_Task设置成当前线程。如果TCD_Execute_Task没被修改,则被中断的任务恢复运行;如果有更高级的任务ready,则等高级任务挂起后,被中断的任务才能恢复运行。iii)

36、、中断产生时,有一个高级中断服务程序HISR在运行。如果本次中断的HISR优先级比当前的HISR(TCD_Execute_HISR)优先级低,则LISR激活HISR时不会修改TCD_Execute_HISR,中断恢复后,继续执行中断前的HISR(上下文保护时已给当前的HISR建立了一个中断类型的堆栈帧),然后再根据HISR的优先级进行调度。如果本次中断的HISR优先级比当前的HISR优先级高,则LISR激活HISR时会修改TCD_Execute_HISR,中断恢复后,优先执行优先级高的HISR,等到TCT_Shedule调度到本次被中断的HISR时,被中断的HISR接着被中断的部分继续执行。、

37、HISR的激活(TCT_Active_HISR) HISR是在LISR中被激活的,TCT_Active_HISR只是激活由LISR指定的HISR以及修改TCD_Execute_HISR,并不真正地执行HISR,HISR在TCT_Shedule中才被真正地调度执行。激活HISR首先根据激活次数hisr-tc_activation_count来判断该HISR是否已被激活。如果HISR已被激活,则只将激活次数tc_activation_count加1。如果该HISR没有被激活过,且该HISR的优先级激活链表为空,则将该HISR挂到本优先级激活链表上,同时根据HISR的优先级决定是否修改TCD_Exe

38、cute_HISR。如果该HISR没有被激活过,且该HISR的优先级激活链表非空,则直接将该HISR挂到本优先级激活链表的尾指针,不用修改TCD_Execute_HISR,因为本优先级激活链表的头指针就有可能是TCD_Execute_HISR,或者有更高优先级的HISR已被激活。、HISR的调度中断恢复后,如果当前的TCD_Execute_HISR是被中断停下来的HISR,则经TCT_Shedule调度后,被中断的HISR恢复运行。如果TCD_ Execute_HISR已被修改,是一个新的HISR,则TCT_Shedule会将该HISR放入TCT_HISR_Shell中进行调度。TCT_HIS

39、R_Shell完成HISR的调度。首先循环调度当前的TCD_Execute_HISR,也就是循环执行HISR创建时用户指定的入口函数,直至其激活次数tc_activation_count等于0。如果TCD_Execute_HISR只被激活了一次,则HISR的入口函数只会执行一次。TCD_Execute_HISR调度完毕(激活次数为0),如果TCD_Execute_HISR所在优先级的激活链表只有这一个HISR,则将本优先级激活链表清空(将TCD_Active_HISR_HeadsX及TCD_Active_HISR_TailsX置为NU_NULL),然后从激活链表头指针数组TCD_Active_

40、HISR_Heads中按优先级顺序搜索到一个已被激活的最高优先级的HISR,由此来修改TCD_Execute_HISR,如果没有其他HISR被激活,则TCD_Execute_HISR为空指针。TCD_Execute_HISR调度完毕,如果TCD_Execute_HISR所在优先级激活链表不只这一个HISR被激活,则将TCD_Execute_HISR从本优先级激活链表删除,将本优先级下一个激活的HISR设置为TCD_Execute_HISR。最后,为当前正在调度的HISR建立一个solicited类型的堆栈帧,将堆栈指针保存在HISR控制块中,清除当前线程TCD_Current_Thread,将

41、堆栈切换到系统堆栈顶部TCD_System_Stack。然后将控制权交给TCT_Shedule重新调度,如果还有其它的HISR被激活,则重复上面的过程;否则进入任务的调度,或在TCT_Shedule死循环,等待HISR被激活或任务ready。一个完整的中断处理流程如下:1.3.3 操作系统数据结构的保护/ben:与uc/os中的OS_ENTER_CRITICAL和OS_EXIT _CRITICAL有什么不同?其好象是通过改变优先级而不是关中断来实现的,那么其中有没有类似的保护呢由于Nucleus操作系统的线程是可以抢占的,高优先级的任务可以抢占低优先级的任务,HISR可以抢占任务,HISR之间

42、也可以抢占。如果某个低优先级的任务正在通过系统调用对操作系统的某个数据结构进行操作,比如:正在修改已创建任务链表TCD_Created_Tasks_List,或正在往某个消息管道Pipe中填消息(要修改消息管道的数据成员),这时发生了任务抢占,如果发生抢占的高优先级任务也要修改同一数据结构,就必须等待低优先级的任务完成修改数据结构的系统调用后,再让高优先级的任务运行,否则就会破坏操作系统的数据结构。这一机制是通过操作系统结构保护(Protect)实现的。任务运行时如果要修改操作系统的数据结构,就要通过系统调用TCT_Protect (TC_PROTECT *protect) 把该数据结构的保护

43、结构保护起来,当任务对该数据结构的操作结束就会通过系统调用TCT_Unprotect (void)或TCT_Unprotect_Specific (TC_PROTECT *protect) 来释放保护结构。如果低优先级的任务没有释放保护结构之前,发生了任务抢占,高优先级的任务抢占了低优先级的任务,如果高优先级的任务也要修改同一系统数据结构,那么在做系统调用TCT_Protect时,就会发现该数据结构的保护结构已经被另一个线程拥有,当前的线程就会暂时被挂起,将控制交给拥有保护结构的线程,等待拥有该保护结构的线程释放掉保护结构后,也就是拥有保护结构的线程在做TCT_ Unprotect时,高优先级

44、的任务才能真正地把控制权抢占过来。Nucleus共有四种跟线程调度有关的比较重要的保护结构:TCD_List_Protect:已创建任务链表的保护结构;TCD_System_Protect:系统保护结构,用于任务调度;TCD_LISR_Protect:用于LISR的创建和删除;TCD_HISR_Protect:用于HISR的创建和删除; 保护结构的结构体是这样定义的:typedef struct TC_PROTECT_STRUCTTC_TCBtc_tcb_poiter;/* 拥有保护结构的线程指针 */UNSIGNEDtc_thread_waiting;/* 有线程在等待该保护结构的标志 */

45、TC_PROTECT;、数据结构的保护(TCT_Protect)当线程在操作的数据结构不想因为线程的抢占而破坏时,就要申请对该数据结构的保护,比如:在创建HISR时,就要申请HISR链表的保护TCT_Protect(&TCD_HISR_Protect);在链表插入完成后,就要调用TCT_Unprotect( )释放当前线程拥有的保护结构。另外,如果线程正在执行的系统调用不想因为线程的抢占而中断时,就要申请系统保护结构的保护TCT_Protect(&TCD_System_Protect),比如:任务的恢复、任务的挂起、消息管道的收发等,这些操作都要申请系统保护结构,当系统调用结束后,就要调用TC

46、T_Unprotect( )释放当前线程拥有的保护结构。TCT_Protect首先判断要保护的结构是否被其它线程拥有,如果没有其它线程拥有要保护的结构,则将当前线线程TCD_Current_Thread赋给protect-tc_tcb_pointer,表示当前线程要占用该保护结构,清除保护结构的线程等待标志,将保护结构的指针送给TCD_Current_Thread-tc_current_protect,表示当前线程拥有一个保护结构,然后返回。如果其它线程拥有要保护的结构,则要调用TCT_Schedule_Protectd,直至其它线程释放掉该保护结构,然后重复上面的处理,表示当前线程拥有该保护

47、结构。、保护结构的释放(TCT_Unprotect)当拥有保护结构的线程处理完系统调用后,就要释放被保护的结构。保护结构的释放有两个系统调用:TCD_Unprotect释放的是线程当前的保护结构tc_current_protect;TCD_Unprotect_Specific释放的是用户或操作系统指定的保护结构。TCD_Unprotect首先判断当前线程是否拥有一个保护结构,再判断保护结构的线程等待标志,看看是否有其它线程在等待该保护结构,如果这些条件都成立,则将控制权交给TCT_Control_To_System。TCT_Control_To_System为当前线程创建一个Solicited

48、类型的堆栈帧,清除当前线程拥有的保护结构以及保护结构上挂的线程,清除TCD_Current_Thread,将堆栈从线程堆栈切换到系统堆栈,然后将控制权交给TCD_Schedule重新进行调度,从而引起TASK或HISR的竞争(线程的抢占)。如果没有其它线程在等待该保护结构,则直接清除当前线程的tc_current_protect,以及保护结构的tc_tcb_pointer,然后返回。 TCD_Unprotect_Specific首先清除指定保护结构上挂的线程tc_tcb_pointer,如果没有其它线程在等待该保护结构就直接返回。如果还有其它线程在等待该保护结构,则为当前线程建立一个Solic

49、ited类型的堆栈帧,清除当前线程TCD_Current_Thread,将堆栈从线程堆栈切换到系统堆栈,然后将控制权交给TCD_Schedule重新进行调度,从而引起TASK或HISR的竞争(线程的抢占)。 、保护的调度(TCT_Schedule_Protected)保护结构的调度也就是调度拥有保护结构的线程,直至其释放保护。TCT_Schedule_Protected首先为想得到保护结构的当前线程建立一个Solicited类型的堆栈帧,将拥有保护结构的线程置为当前线程TCD_Current_Thread,将堆栈切换至系统堆栈,将控制权交给TCT_Control_To_Thread,这里将恢复

50、运行因线程抢占而挂起的拥有保护结构的线程,等其处理完相关数据结构后,就会调用TCD_Unprotect来释放保护结构,释放时就会发现还有另一个线程在等待该保护结构,然后控制权就会交给TCD_Schedule重新进行调度,引起线程之间的竞争。注意:TCT_Schedule_Protected并没有修改TCD_Execute_Task及TCD_ Execute_HISR,只是临时地将拥有保护结构的线程置为当前线程,当拥有保护结构的线程释放保护后,TCD_Schedule又会恢复运行想得到保护结构的TCD_Execute_Task或TCD_ Execute_HISR。数据结构保护的处理示意图如下:N

51、ucleus操作系统不具备优先级自动逆转的功能,它有自己的解决方法。当高优先级的线程不能获得临界系统资源时,高优先级的任务并没有挂起,所有在此优先级之下的线程就都不能运行,Nucleus会把拥有临界系统资源的低优先级线程临时地升为当前线程TCD_Current_Thread(相当于临时把优先级升为最高),当低优先级的线程释放临界系统资源后,高优先级的任务马上会恢复运行。VxWorks是通过互斥信号灯来实现优先级自动逆转的,操作系统能自动提升获得此信号灯的所有任务的优先级,与获得此信号灯的最高优先级任务相同。如果所有任务没有设置信号灯选项,它们的工作方式是这样的:优先级低的任务获得一个临界资源且

52、正在运行,高优先级任务因为没有获得这一临界资源而挂起,这时一个中优先级任务就绪,如果低优先级的任务是可以抢占的,它就会抢占低优先级的任务,造成高优先级任务没有机会运行。如果低优先级和高优先级的任务都设置了互斥信号灯,操作系统会自动提高低优先级任务的优先级和高优先级任务相同,这样就不会出现上面的问题。1.4 任务间通信Nucleus任务间的通信机制有邮箱(mailboxes),消息对列(queues),消息管道(pipes)。它们之间的主要不同之处在于使用的消息结构不同。/邮箱(mailboxes)消息长度有限制,最多只能容纳一条4个长字(16 bytes)的消息/消息对列(queues)消息长

53、度单位为一个长字,4 bytes/消息管道(pipes) 消息长度单位为一个字节Nucleus的邮箱(mailboxes)提供的是单个消息的简单通信机制。每个邮箱最多只能容纳一条4个长字(16 bytes)的消息。邮箱可以动态地创建和删除,邮箱的数目不受限制。Nucleus的队列(queues)提供的是传送多个消息的通信机制,一条消息由一个以上的长字组成,支持定长和变长的消息类型。队列也可以动态地创建和删除,队列的数目不受限制。Nucleus的管道(pipes)提供的也是传送多个消息的通信机制,一条消息由由一个以上的字节组成,支持定长和变长的消息类型。管道也可以动态地创建和删除,管道的数目不受

54、限制。 Nucleus的队列和管道是十分类似的,不同之处在于:队列的消息按长字来访问,管道的消息按字节来访问。下面我们只拿管道来分析Nucleus的任务通信机制。1.4.1 消息管道(Pipes)管道支持定长和变长的消息类型,消息格式的类型在创建时就已经固定。定长消息的管道比较好管理,每条消息都具有固定的长度;变长消息的管道管理起来就相对复杂一点,每条消息前还要加四个bytes用来指示消息的长度,一条变长的消息有时不是位于连续的内存空间,也就是当管道写指针指到管道尾时,一条变长消息就会分成管道的首尾两块。、管道的创建(PIC_Create_Pipe)管道创建时,首先要对以下比较重要的管道控制块

55、数据成员赋初值,用来确定管道的大小和消息类型:pipe-pi_fifo_suspend:NU_FIFO表示任务在管道上的挂起顺序是按先入先出(FIFO)的顺序;NU_PRIORITY表示任务在管道上的挂起顺序是按照优先级的顺序。pipe-pi_fixed_size:NU_VARIABLE_SIZE表示管道的消息类型是变长的;NU_FIXED_SIZE表示管道的消息类型是定长的。pipe-pi_message_size:对于定长的消息类型,表示每条消息的大小;对于变长类型的消息,表示最长消息的大小。pipe-pi_pipe_size:管道总的大小。pipe-pi_start,pipe-pi_en

56、d:管道在内存空间的起始和终止地址指针。pipe-pi_read,pipe-pi_write:管道的读写指针。pipe-pi_suspend_list:管道上的挂起任务链表头指针;pipe-pi_urgent_list:管道上发送紧急消息的挂起任务链表头指针。pipe-pi_tasks_waiting:管道上等待任务的个数。注意:只有任务才能挂起在管道上,HISR是不能挂起的,HISR在管道上收发消息的时候都要使用NU_NO_SUSPEND选项。然后,将该管道插入已创建管道链表PID_Created_Pipes_List。、管道的删除(PIC_Delete_Pipe)管道的删除首先要把该管道从

57、已创建管道链表中删除。其次,将管道挂起任务链表pipe-pi_suspend_list上被挂起的任务(在管道上收发消息时被挂起)全部恢复(TCC_Resume_Task),给所有的任务返回状态NU_PIPE_DELETED。然后,将管道紧急消息挂起链表pipe-pi_urgent_list上被挂起的任务(做PIS_Send_To_Front_Of_Pipe调用时被挂起)全部恢复,给所有任务返回状态NU_PIPE_DELETED。如果被恢复的任务优先级比做管道删除系统调用的任务优先级高,且做该系统调用的任务可以抢占,则控制权会交给TCT_Control_To_System,发生任务的抢占。 、往

58、管道发送消息(PIC_Send_To_Pipe)PIC_Send_To_Pipe负责往指定的管道发送消息,消息的长度由用户指定。如果管道上有一个或更多的任务在等待消息,则发送的消息直接拷贝至第一个等待任务的消息域,同时挂起的任务被恢复,如果被恢复的任务优先级比当前任务的优先级高,且当前任务可以抢占,则控制权会交给TCT_Control_To_System,发生任务的抢占。如果管道上没有足够的空间来存放消息,则往管道发送消息的任务就会被挂起(可以选择挂起或不挂起)。如果管道上有空间存放消息,则要发送的消息就会copy至管道中。往管道发送消息的处理流程如下: 、从管道中接收消息(PIC_Recei

59、ve_From_Pipe)PIC_Receive_From_Pipe负责从指定的管道中接收消息,接收到的消息的实际长度放在actual_size中。如果管道中没有消息,任务可以选择挂起与否。如果有任务做PIS_Send_To_Front_Of_Pipe系统调用发送紧急消息到管道中,由于管道中没有空间而被挂起,则紧急消息会copy至接收任务的接收消息域,被挂起的任务马上会恢复,如果当前任务可以抢占且被挂起的任务比当前任务优先级高,则控制权会交给TCT_Control_To_System,发生任务抢占。如果管道中有消息,则管道中读指针指向的消息会copy 至接收任务的接收消息域.。如果有发送消息的

60、任务挂起在管道上,且管道中又有空间存放其要发送的消息,则被挂起的任务会恢复,其要发送的消息会copy至管道中。如果当前任务可以抢占且被挂起的任务比当前任务优先级高,则控制权会交给TCT_Control_To_System,发生任务抢占。从管道接收消息的处理流程如下:拿两个任务举列来说明消息管道的收发过程(假设没有其它任务在运行):、两个任务具有不同的优先级,高优先级任务不停地发送,低优先级任务不停地接收。 、两个任务具有不同的优先级,低优先级任务不停地发送,高优先级任务不停地接收。 、两个任务具有相同的优先级,分时间片运行。1.5 任务的同步Nucleus提供:信号量(semaphores)、

61、事件组(event groups)和信号(signals)来实现任务的同步。其中信号量和事件组是公共的工具,和任务及其他操作系统对象的联系由用户来定;而信号只和特定的任务相关。下面分别说明。 信号量(semaphores) 信号量提供一种机制来控制操作系统临界资源的分配。两个基本操作是obtain和release,obtain为减少信号量,release为增加信号量。信号量最常见的应用就是资源的分配,一个任务获得某一资源,信号量就减少,任务释放资源,信号量就增加。当一个任务试图去obtain一个为0的信号量时,任务就会挂起,直到此信号量被释放,任务才恢复。当多个任务试图obtain一个信号量时

62、,任务就会根据信号量创建时支持的是FIFO挂起方式还是priority挂起方式来决定任务挂起的顺序。Nucleus避免死锁的方法是当应用程序使用信号量时强加一些规则给应用程序,如禁止任务在同一时间占用多个信号量等。当一个低优先级的任务拥有高优先级的任务所需要的信号量时,就会发生优先级逆转,以避免任务被无限期地挂起。Nucleus可以动态地创建和删除信号量,信号量创建时的初始值也可以是0到4,294,967,294(0xfffffffe)的任意值。 事件组(event groups) 事件组提供一种机制来指示一个特定的系统事件。每个事件组由32bit组成,每个bit叫事件标志,代表一个事件。事件

63、标志位可以用逻辑与/或的组合来设置或清除,Nucleus可以动态地创建和删除事件组。 信号(signals) 信号和事件组类似,但是在操作上有明显的区别。事件组是用来同步的,在指定的服务请求执行之前,任务不会识别存在的事件标志。信号以异步的方式来操作,当一个信号出现,任务就中断执行,转入执行由任务预先定义的信号处理程序(signal_handling routine)。每个任务可以处理32个信号,每个信号由1bit来表示。 信号处理程序类似于高级中断服务程序,它不会被新的信号中断。所有新信号的处理都要在当前的信号处理完成之后。 任务创建时所有信号都是disable的,个别的信号可以由任务动态地enable和disable。 当信号处理程序被唤醒时,信号就被清除。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!