关闭

关闭

关闭

封号提示

内容

首页 Nucleus实时操作系统分析报告.doc

Nucleus实时操作系统分析报告.doc

Nucleus实时操作系统分析报告.doc

上传者: 雷中华 2010-12-18 评分 0 0 0 0 0 0 暂无简介 简介 举报

简介:本文档为《Nucleus实时操作系统分析报告doc》,可适用于IT/计算机领域,主题内容包含Nucleus实时操作系统分析报告Nucleus实时操作系统分析报告目录TOCo一、Nucleus的内核(Kernel)系统启动初始化线程线程调度任符等。

Nucleus实时操作系统分析报告Nucleus实时操作系统分析报告目录TOCo一、Nucleus的内核(Kernel)系统启动初始化线程线程调度任务的调度中断的调度操作系统数据结构的保护任务间通信消息管道(Pipes)任务的同步定时器内存管理输入输出设备驱动Nucleus内核总结:二、Nucleus的开发工具NucleusCNucleusMNTNucleusVNETNucleusPCNucleusFILENucleusCLIBNucleusGRAFIXNucleusEDENucleusUDBNucleusDEBUG第三方产品的支持SDSSingleStep三、Nucleus的网络支持Internet套件NucleusNETNucleusSNMPNucleusRMONNucleusSPANNucleusWebServNucleusJviNucleusEPILOGUENucleus实时操作系统分析报告Nucleus实时操作系统是AcceleraterTechnology公司开发的嵌入式RTOS产品只需一次性购买Licenses就可以获得操作系统的源码。Nucleus购买的灵活性比较大:KernelNetworkingFileSystemWebTechnologyTargetDebugger可以分开购买如果我们只需要微内核的话只要购买Kernal和Debugger当前的下位机就是这样配置的。但是如果以后我们要开发接入服务器和IPPhone的话就必须购买一堆网络协议上位机要用的话还要购买文件系统。VxWorks就不是这样其OS基本部分中已经包括了很多基本的网络协议只有一些比较特殊的网络协议需要单独购买比如:SNMPOSPFIPX等。当然Nucleus的好处在于各层协议都提供的是源码Vxworks则不提供。Nucleus的另一大好处是程序员不用写板支持软件包(BSP)因为操作系统已经开放给程序员不同的目标板在操作系统BOOT时可以通过修改源码进行不同的配置。对于程序员来说写BSP是一项比较繁琐的任务有了OS的源码这项工作就简单多了同时调试时也可以跟踪到中断、寄存器那一级简化了硬件的调试。VxWorks和pSOS都必须购买标准的BSP模板工具来写板支持软件包开发工作量就比较大了。Nucleus对CPU的支持能力比较强支持当前流行的大多数RISC、CISC、DSP处理器比如:x(实时、保护模式)、xxx、PowerPC、i、MIPS、SH、ARM、ColdFire。Nucleus系统对于不同的处理器操作系统的源码大部分是相同的只有的源码是用汇编写的跟CPU有关。如果使用另外的CPU则只用修改、个汇编程序就可以进行移植但是对于不同系列的处理器它们的Compiler、Linker、Debugger是不同的(要另外购买)而且编译预处理的过程也是不一样的这就要投入一部分力量去进行操作系统的移植。当然这也是Nucleus的一个优点VxWorks和pSOS都没有源码换一个系列的CPU操作系统又要重新购买实际上他们OS开发的工作量并没有多少但却造成了用户的重复性投资。当然Nucleus作为一个非主流的操作系统其自身也存在着很多不足之处比如:实时性不够、定时中断管理不可靠、IO管理太简单、操作系统的调试工具太少等。下面分别说明。、​ Nucleus的内核(Kernel)Nucleus的核心是一个实时的多任务内核NucleusPLUS具有以下特性(AcceleraterTechnology公司宣称的):可移植性:NucleusPLUS可用于大多数流行的处理器。因为NucleusPLUS主要是用标准C写的移植到新的处理器系列相对很直接。这也就意味着用NucleusPLUS开发的应用程序也具有很高的可移植性。可用性:不像其他的商业内核NucleusPLUS的系统调用名直接表明了它的功能。比如你可以通过NuCreateTask系统调用来创建一个任务。NucleusPLUS的系统调用都设计成具有类似的入口参数和返回值类型。NucleusPLUS的对象都不隐式地和别的对象相关。比如邮箱和任务之间是不相关的。因此用户可以利用多个NucleusPLUS对象之间的结合形成混合系统调用。配置:NucleusPLUS最终是以C库的形式提供给用户你可以选择所需要的部分链入到你的应用程序中。但是其可裁剪性并不好如果某些系统功能不要的话必须在Nucleush的头文件中加一些宏定义比如定义:#defineNUENABLESTACKCHECK那么每个系统调用时都要进行堆栈检查否则就不进行堆栈检查可以删除的功能并不多。VxWorks的可裁剪性就强多了有多个独立模块微内核K最小系统<K配全了可达到几百K。特性:、快速响应时间:对临界资源的检测时间不依赖于占有该临界资源的线程执行时间的长短一旦低优先级线程释放掉临界资源(不管其是否执行完)高优先级线程就会抢占运行。、每个任务的执行时间和其他任务的处理时间无关。、较高吞吐量:随着任务数目的增多任务的调度时间为常数。、可扩展性:利用现有系统调用的结合可得到新的系统调用。NucleusPLUS提供其他实时内核都具有的系统服务比如:任务控制、任务通信、任务同步、内存管理、可编程的定时器、标准的输入输出设备接口等。对任务的调度依赖优先级、时间片的方法所有的操作系统对象(任务、邮箱、队列、管道等)都可以动态地创建和删除。创建一个对象时要指定指定其控制块的内存区域和其它的数据要求(堆栈空间等)。Nucleus在任务调度时首先查看任务是否可以抢占如果不能抢占则一直执行到任务完成或任务放弃时间片否则依靠优先级进行调度先调入优先级最高的任务对于优先级相同的任务则分享时间片、轮流调度。​ 系统启动对于K系列的CPU如果采用CrossCodeC编译器的话Nucleus使用的是CrossCodeC的启动函数标号START是系统的入口点。START标号在文件starts中用汇编及CrossCodeC的宏指令写的。主要完成中断向量表指针VBR和寄存器基址MBAR的初始化以及外部RAM各分区的初始化这些分区包括:ramdatamallocinitsysmemory等。系统低级初始化完成后控制就交给初始化线程INTInitialize。​ 初始化线程初始化线程是系统开始执行的第一个线程线程的入口是INTInitialize同时也是系统的主函数具有另外的标号名main。系统初始化首先完成硬件寄存器的配置包括:存储器片选(CS~CS)软件看门狗SWT系统周期定时器PITA口、B口、C口引脚功能设定串行通信控制器的初步配置等。这些硬件的配置跟目标板有关需要用户自己用汇编来写。其次将系统堆栈指针TCDSystemStack初始化为堆栈区stack的顶部同时在系统内存区sysmemory中拿出TMDHISRStackSize大小的一片内存用作高级中断服务程序的堆栈HISRSTACK。然后控制交给INCInitialize。INCInitialize首先完成操作系统数据结构的初始化包括:线程控制、邮箱、队列、管道、信号量、事件、分区内存、动态内存、定时器、IODriver等。其次调用ApplicationInitialize这一函数由用户编写完成任务、消息队列等的创建、中断的注册以及应用程序的初始化。当所有的初始化都完成后INCInitialize调用TCTSchedule开始线程的调度。​ 线程调度TCTSchedule是线程调度的入口负责将控制权交给具有最高优先级的高级中断服务程序HISR(TCDExecuteHISR)或处于就绪状态的最高优先级任务(TCDExecuteTask)。当没有任务或HISR执行时线程调度就在TCTSchedule中做死循环等待TASK或HISR就绪。HISR的优先权比任务高一旦有HISR就绪则当前调度的任务将会被挂起优先调度HISR。一旦有TASK或HISR就绪控制就会交给TCTControlToThread在这里将TCDExecuteHISR或TCDExecuteTask设置为当前线程TCDCurrentThread启动任务时间片定时器根据线程的不同堆栈类型恢复堆栈然后执行RTS或RTE指令将控制权交给线程。Nucleus的线程有两种类型的堆栈帧:任务创建时要建立一个初始堆栈帧线程入口是TCCTaskShell该Shell执行任务的入口程序通常任务的执行是一个死循环不停地在等待消息或事件如果没有消息或事件任务就会挂起否则往下执行。如果任务在Shell调度中返回则表示该任务已执行完毕将任务终止其状态置为NUFINISHED。HISR创建时也要建立一个初始堆栈帧线程入口是TCCHISRShell。HISRShell调度的是当前具有最高优先级的HISR直至TCDExecuteHISR的激活计数器tcactivationcount为才调度同一优先级或低优先级的其它HISR。HISRShell对HISR调度就是执行HISR的入口程序然后根据激活计数器循环调度HISR是不能被挂起的。任务的调度任务的调度需要用到以下比较重要的数据结构:TCDCreatedTasksList:已创建任务链表的头指针。TCDPriorityList:任务控制块TCB的指针数组每个元素是优先级~就绪任务链表的头指针。TCDPriorityGroups:按位来定义的长字对应组优先级每bit是一组负责个优先级如果其中任意一个优先级有任务就绪则该bit置。TCDSubPriorityGroups:子优先级组的位映像图每一元素对应一组优先级。比如TCDSubPriorityGroups对应优先级~bit~bit分别表示优先级~。TCDHighestPriority:当前就绪任务的最高优先级并不表示当前正在运行任务的优先级如果该任务不能抢占。TCDExecuteTask:当前正在执行的任务指针。TCDCurrentThread:当前正在执行的线程指针(TASKorHISR)。Nucleus的任务具有五种状态:executingreadysuspendedfinishedterminated。Executing:任务正在执行但是其任务控制块TCB中的状态tcstatus仍为NUREADY只是该任务的指针等于当前线程TCDCurrentThread。Ready:任务已就绪但是有其它任务在运行。Suspended:任务在等待请求服务完成的过程中被挂起一旦请求服务完成任务就会迁移至ready状态。Finished:该任务的处理已经完成(在TCCTaskShell调度中返回)。一旦任务处于这种状态就不能再执行了除非任务被复位。Terminated:任务被Killed。一旦任务处于这种状态就不能再执行了除非任务被复位。Nucleus的每个任务都具有一个~的优先级表示最高优先级表示最低优先级对于相同优先级的任务分时间片运行不同优先级的任务则可以发生抢占。任务的调度涉及到:创建任务、删除任务、复位任务、终止任务、恢复任务、挂起任务等。、创建任务(TCCCreateTask)任务的创建一般是在ApplicationInitialize中进行当然也可以在其它任务中动态地创建和删除任务。任务创建的流程如下:、删除任务(TCCDeleteTask)对任务删除时默认任务是处于finished或terminated状态。将任务删除主要是将任务从已创建任务链表中删除并不能释放与任务相关连的控制块(TCB)和堆栈。、复位任务(TCCResetTask)当任务处于finished或terminated状态时才能对该任务执行复位否则返回NUNOTTERMINATED表示任务没有结束或终止。任务复位主要是将任务控制块中的数据成员重新赋初值重新创建任务堆栈(任务堆栈复位)将任务状态置为无条件挂起状态NUPURESUSPEND。、终止任务(TCCTerminateTask)如果要终止的是当前任务(TCDCurrentThread)则直接将任务挂起将任务状态置为NUTERMINATED。如果要终止的不是当前任务则要对任务状态进行判断。如果任务已经处于finished或terminated状态则什么也不做。如果任务处于ready状态则直接将任务挂起将任务状态置为NUTERMINATED。如果任务处于suspended状态则必须释放和该任务相关的所有保护结构后才能将任务终止。、恢复任务(TCCResumeTask)如果任务可以获得执行所需要的系统资源比如:对于做NUSendToPipe系统调用的任务如果消息管道已有空余空间或者做NUReveiveFromPipe系统调用的任务如果管道中有消息那么挂起在该管道上的任务就会恢复。如果任务挂起类型与请求恢复类型一致则把任务状态置为NUREADY将任务插入就绪任务优先级链表TCDPriorityListtask>tcpriority设置优先级组TCDPriorityGroups和子优先级组TCDSubPriorityGroupstask>tcpriority中本任务优先级对应的bit指示本优先级有任务ready。如果要恢复的任务优先级比当前最高优先级TCDHighestPriority要高且当前任务TCDExecuteTask可以抢占则将要恢复的任务置为当前任务同时会产生任务抢占返回NUTRUE否则返回NUFALSE。、挂起任务(TCCSuspendTask)如果任务不能获得执行所需要的系统资源比如:对于做NUSendToPipe系统调用的任务如果消息管道已满或者做NUReveiveFromPipe系统调用的任务如果管道已空那么任务就会被挂起。任务挂起首先判断要挂起的是不是当前任务TCDCurrentThread如果不是挂起当前任务则要释放任务的当前保护结构tccurrentprotect。其次如果任务的状态为该任务优先级就绪任务链表TCDPriorityList中只有这一个任务ready则要清空该优先级就绪任务链表同时要清除子优先级组TCDSubPriorityGroups和优先级组TCDPriorityGroups对应的bit。如果要挂起的任务具有最高优先级则要根据优先组和子优先组重新搜索最高优先级如果其他组中没有任务就绪则TCDHighestPriority=。然后根据最高优先级重新调整TCDExecuteTask如果最高优先级为则TCDExecuteTask=NU。如果任务的状态为NUREADY且该任务优先级就绪任务链表TCDPriorityList中不只这一个任务ready则将该任务从优先级就绪任务链表中删除不用修改子优先级组和优先级组另外也不用调整最高优先级TCDHighestPriority只是利用最高优先级重新调整TCDExecuteTask。如果要挂起的是当前线程TCDCurrentThread则将控制交给TCDControlToSystem在TCDControlToSystem中给当前线程创建一个solicited类型的堆栈帧线程入口是调用TCDControlToSystem的下一条指令任务恢复时从这条指令开始继续执行。TCDControlToSystem随后又将控制交给TCTScheduleTCTSchedule根据TCDExecuteHISR或TCDExecuteTask开始下一轮的任务调度如果调度过程中发现挂起任务需要的系统资源可以满足就会把任务恢复按照优先级重新调度。任务挂起流程如下:​ 中断的调度Nucleus的中断分为管理的和非管理的中断。管理的中断需要向操作系统注册该中断向量中断产生后通过该中断向量注册的低级中断服务程序(LISR)来激活高级的中断服务程序(HISR)。LISR主要完成硬件中断的处理及激活HISR。HISR的调度类似于任务具有优先级可以使用大多数Nucleus的系统调用。非管理的中断则不需要通过操作系统进行管理直接将中断服务程序挂到中断向量表上上下文的保存与恢复都要用户自己来做该中断自己不能嵌套最好不要被管理的中断再次中断否则会引起堆栈出错而且非管理的中断不能使用绝大多数的Nucleus系统调用因为它可能会破坏操作系统某些保护的数据结构(当有线程在运行时)。非管理的中断适用于那些比较频繁的中断如果通过操作系统来管理这些中断的话其上下文保存与恢复的时间就比较长中断的实时性就不能满足要求。非管理的中断比较简单类似于以前我们写的中断服务程序这里就不多说。下面我们讨论的中断的调度都指的是Nuclesu管理中断的调度。中断的调度需要用到以下比较重要的数据结构:TCDRegisteredLISRs:对应的个中断向量。表示该中断向量没有注册是操作系统不能处理的中断(UnhandledInterrupt)非表示该中断向量已注册且其值为在LISR函数指针数组TCDLISRPointers中的索引下标。TCDLISRPointers:LISR函数指针数组指向当中断产生时要调用的低级中断服务程序LISR的入口函数。TCDInterruptCount:表示有多少个中断服务程序(ISRs)正在进行处理。:没有中断:只有一个中断>:中断嵌套。TCDInterruptLevel:允许中断的级别用来给的状态寄存器SR赋值。x表示屏蔽所有中断x表示屏蔽级及级以下的中断表示打开所有中断。TCDUnhandledInterrupt:系统出错时表示不能处理的中断向量号。TCDCreatedHISRsList:已创建的HISR链表的头指针。TCDActiveHISRHeads:对应HISR优先级~每个数组元素是该优先级已激活HISR链表的头指针。TCDActiveHISRTails:对应HISR优先级~每个数组元素是该优先级已激活HISR链表的尾指针。TCDExecuteHISR:当前正在执行或要执行的具有最高优先级的HISR指针。每个HISR具有一个~的优先级表示最高优先级表示最低优先级相同优先级的HISR按照先入先出的顺序处理优先级不同的HISR按照优先级的高低进行调度。HISR是不能被挂起的因此其所有的系统调用都要加上NUNOSUSPEND参数。中断的调度包括:中断向量的注册、HISR的创建与删除、上下文的保护与恢复、LISR的执行、HISR的激活以及HISR的调度等。、中断向量的注册(TCCRegisterLISR)一个中断要通过操作系统管理起来首先要将其中断向量通过系统调用NURegisterLISR(INTvector,VOID(*newlisr)(INT),VOID(**oldlisr)(INT))注册起来。该系统调用的第个参数是LISR的入口函数指针也就是中断产生后要执行的LISR。中断的注册首先要判断该中断向量是否已经注册过。如果该中断已经注册过则利用TCDRegisteredLISRs索引到该中断向量在LISR函数指针数组TCDLISRPointers中的下标然后将新的LISR(newlisr)填入TCDLISRPointers。如果该中断向量没有注册过则在LISR函数指针数组TCDLISRPointers中找出一个没有使用的单元将newlisr填入该单元同时将该单元的下标填入该中断向量在TCDRegisteredLISRs中对应的单元。另外还要判断INTLoadedFlag标志如果该标志为则要替换掉当前的中断向量表否则不修改当前的中断向量表。、HISR的创建(TCCCreateHISR)HISR也就是中断产生后要在LISR中激活的高级中断服务程序。HISR的创建比任务创建简单不用进行设置任务状态、恢复任务等操作只需创建一个HISR控制块HCB初始化HCB中的一些参数为HISR创建一个Solicited类型的堆栈帧将该HISR的指针挂到已创建HISR链表TCDCreatedHISRsList同时分配一个用户指定的入口函数指针该函数用来完成真正的中断处理。、HISR的删除(TCCDeleteHISR)HISR的删除默认HISR处于非激活状态仅仅是将HISR从已创建HISR链表TCDCreatedHISRsList中删除并清除HISRID标志并不能释放与HISR相关的内存(控制块、堆栈等)同时也不影响HISR的激活对HISR的调度可能会产生微小的影响(由于HISRID被清除)。一般来说HISR的删除没有什么意义除非把跟HISR相关的中断也关掉。、上下文的保护(TCTInterruptContextSave)通常的中断服务程序对要用到的寄存器都要进行堆栈保护Nucleus操作系统除了做这些外还要对当前线程进行保护使得高级中断服务程序HISR可以抢占任务让HISR得到快速的响应。Nucleus在系统空闲(没有线程运行)时中断堆栈使用的是系统堆栈TCDSystemStack如果有线程(任务或HISR)在运行使用的是任务或HISR堆栈上下文保护完成之后则将堆栈切换到系统堆栈TCDSystemStack。上下文保护首先将所有中断屏蔽掉等保护完成之后再将中断打开。其次判断中断计数器TCDInterruptCount如果本次中断是中断嵌套则将TCDInterruptCount加然后返回。如果本次中断不是中断嵌套则判断当前有没有线程在运行如果当前有线程在运行则为当前线程建立一个Interrupt类型的堆栈帧将当前的堆栈指针保存在线程控制块中再将堆栈指针切换到系统堆栈顶部TCDSystemStack然后返回如果当前没有线程运行则直接将堆栈指针切换到系统堆栈顶部TCDSystemStack然后返回。、LISR的执行(TCCDispatchLISR)中断上下文保护完成后就要根据中断向量在LISR指针数组TCDLISRPointers中索引到本中断向量的LISR入口函数指针然后执行LISR函数通常LISR要做的只是处理硬件中断及激活HISR。LISR执行完毕则将上下文恢复将控制权交给TCTShedule进行系统调度。要注意的是如果某个中断向量没有注册则会产生系统错误进入系统错误线程ERCSystemError处理这种错误是致命的错误导致整个系统进入一个死循环。、上下文的恢复(TCTInterruptContextRestore)上下文恢复首先判断是不是中断嵌套如果是中断嵌套则将TCDInterruptCount减将堆栈保护的寄存器恢复然后利用RTE指令从中断返回。如果不是中断嵌套则将当前线程TCDCurrentThread清为将堆栈切换到系统堆栈顶部TCDSystemStack将控制交给TCTShedule进行线程的重新调度在这里HISR会抢占任务优先运行。对于没有嵌套的中断恢复并没有执行RTE指令从中断产生的指令往下执行而是将控制交给TCTShedule进行重新调度这类中断恢复可以分三种情况进行分析:i)、系统空闲(做TCDShedule死循环)时产生了中断则上下文恢复后再次运行TCDShedule然后调度由LISR激活的HISR(TCDExecuteHISR)。ii)、中断产生时有一个任务在运行。由于在上下文保护时已经给当前任务建立了一个中断类型的堆栈帧同时LISR运行时没有修改当前任务的状态(没有将当前任务挂起)也没有修改TCDExecuteTask。当中断恢复完成之后TCDShedule首先把TCDExecuteHISR设置为当前线程TCDCurrentThread优先调度HISR如果HISR运行过程中激活了一个比当前被中断任务优先级更高的任务则TCDExecuteTask会被修改等HISR运行完毕则将TCDExecuteTask设置成当前线程。如果TCDExecuteTask没被修改则被中断的任务恢复运行如果有更高级的任务ready则等高级任务挂起后被中断的任务才能恢复运行。iii)、中断产生时有一个高级中断服务程序HISR在运行。如果本次中断的HISR优先级比当前的HISR(TCDExecuteHISR)优先级低则LISR激活HISR时不会修改TCDExecuteHISR中断恢复后继续执行中断前的HISR(上下文保护时已给当前的HISR建立了一个中断类型的堆栈帧)然后再根据HISR的优先级进行调度。如果本次中断的HISR优先级比当前的HISR优先级高则LISR激活HISR时会修改TCDExecuteHISR中断恢复后优先执行优先级高的HISR等到TCTShedule调度到本次被中断的HISR时被中断的HISR接着被中断的部分继续执行。、HISR的激活(TCTActiveHISR)HISR是在LISR中被激活的TCTActiveHISR只是激活由LISR指定的HISR以及修改TCDExecuteHISR并不真正地执行HISRHISR在TCTShedule中才被真正地调度执行。激活HISR首先根据激活次数hisr>tcactivationcount来判断该HISR是否已被激活。如果HISR已被激活则只将激活次数tcactivationcount加。如果该HISR没有被激活过且该HISR的优先级激活链表为空则将该HISR挂到本优先级激活链表上同时根据HISR的优先级决定是否修改TCDExecuteHISR。如果该HISR没有被激活过且该HISR的优先级激活链表非空则直接将该HISR挂到本优先级激活链表的尾指针不用修改TCDExecuteHISR因为本优先级激活链表的头指针就有可能是TCDExecuteHISR或者有更高优先级的HISR已被激活。、HISR的调度中断恢复后如果当前的TCDExecuteHISR是被中断停下来的HISR则经TCTShedule调度后被中断的HISR恢复运行。如果TCDExecuteHISR已被修改是一个新的HISR则TCTShedule会将该HISR放入TCTHISRShell中进行调度。TCTHISRShell完成HISR的调度。首先循环调度当前的TCDExecuteHISR也就是循环执行HISR创建时用户指定的入口函数直至其激活次数tcactivationcount等于。如果TCDExecuteHISR只被激活了一次则HISR的入口函数只会执行一次。TCDExecuteHISR调度完毕(激活次数为)如果TCDExecuteHISR所在优先级的激活链表只有这一个HISR则将本优先级激活链表清空(将TCDActiveHISRHeadsX及TCDActiveHISRTailsX置为NU)然后从激活链表头指针数组TCDActiveHISRHeads中按优先级顺序搜索到一个已被激活的最高优先级的HISR由此来修改TCDExecuteHISR如果没有其他HISR被激活则TCDExecuteHISR为空指针。TCDExecuteHISR调度完毕如果TCDExecuteHISR所在优先级激活链表不只这一个HISR被激活则将TCDExecuteHISR从本优先级激活链表删除将本优先级下一个激活的HISR设置为TCDExecuteHISR。最后为当前正在调度的HISR建立一个solicited类型的堆栈帧将堆栈指针保存在HISR控制块中清除当前线程TCDCurrentThread将堆栈切换到系统堆栈顶部TCDSystemStack。然后将控制权交给TCTShedule重新调度如果还有其它的HISR被激活则重复上面的过程否则进入任务的调度或在TCTShedule死循环等待HISR被激活或任务ready。一个完整的中断处理流程如下:​ 操作系统数据结构的保护由于Nucleus操作系统的线程是可以抢占的高优先级的任务可以抢占低优先级的任务HISR可以抢占任务HISR之间也可以抢占。如果某个低优先级的任务正在通过系统调用对操作系统的某个数据结构进行操作比如:正在修改已创建任务链表TCDCreatedTasksList或正在往某个消息管道Pipe中填消息(要修改消息管道的数据成员)这时发生了任务抢占如果发生抢占的高优先级任务也要修改同一数据结构就必须等待低优先级的任务完成修改数据结构的系统调用后再让高优先级的任务运行否则就会破坏操作系统的数据结构。这一机制是通过操作系统结构保护(Protect)实现的。任务运行时如果要修改操作系统的数据结构就要通过系统调用TCTProtect(TCPROTECT*protect)把该数据结构的保护结构保护起来当任务对该数据结构的操作结束就会通过系统调用TCTUnprotect(void)或TCTUnprotectSpecific(TCPROTECT*protect)来释放保护结构。如果低优先级的任务没有释放保护结构之前发生了任务抢占高优先级的任务抢占了低优先级的任务如果高优先级的任务也要修改同一系统数据结构那么在做系统调用TCTProtect时就会发现该数据结构的保护结构已经被另一个线程拥有当前的线程就会暂时被挂起将控制交给拥有保护结构的线程等待拥有该保护结构的线程释放掉保护结构后也就是拥有保护结构的线程在做TCTUnprotect时高优先级的任务才能真正地把控制权抢占过来。Nucleus共有四种跟线程调度有关的比较重要的保护结构:TCDListProtect:已创建任务链表的保护结构TCDSystemProtect:系统保护结构用于任务调度TCDLISRProtect:用于LISR的创建和删除TCDHISRProtect:用于HISR的创建和删除保护结构的结构体是这样定义的:typedefstructTCPROTECTSTRUCT{TCTCBtctcbpoiter*拥有保护结构的线程指针*UNSIGNEDtcthreadwaiting*有线程在等待该保护结构的标志*}TCPROTECT、数据结构的保护(TCTProtect)当线程在操作的数据结构不想因为线程的抢占而破坏时就要申请对该数据结构的保护比如:在创建HISR时就要申请HISR链表的保护TCTProtect(TCDHISRProtect)在链表插入完成后就要调用TCTUnprotect()释放当前线程拥有的保护结构。另外如果线程正在执行的系统调用不想因为线程的抢占而中断时就要申请系统保护结构的保护TCTProtect(TCDSystemProtect)比如:任务的恢复、任务的挂起、消息管道的收发等这些操作都要申请系统保护结构当系统调用结束后就要调用TCTUnprotect()释放当前线程拥有的保护结构。TCTProtect首先判断要保护的结构是否被其它线程拥有如果没有其它线程拥有要保护的结构则将当前线线程TCDCurrentThread赋给protect>tctcbpointer表示当前线程要占用该保护结构清除保护结构的线程等待标志将保护结构的指针送给TCDCurrentThread>tccurrentprotect表示当前线程拥有一个保护结构然后返回。如果其它线程拥有要保护的结构则要调用TCTScheduleProtectd直至其它线程释放掉该保护结构然后重复上面的处理表示当前线程拥有该保护结构。、保护结构的释放(TCTUnprotect)当拥有保护结构的线程处理完系统调用后就要释放被保护的结构。保护结构的释放有两个系统调用:TCDUnprotect释放的是线程当前的保护结构tccurrentprotectTCDUnprotectSpecific释放的是用户或操作系统指定的保护结构。TCDUnprotect首先判断当前线程是否拥有一个保护结构再判断保护结构的线程等待标志看看是否有其它线程在等待该保护结构如果这些条件都成立则将控制权交给TCTControlToSystem。TCTControlToSystem为当前线程创建一个Solicited类型的堆栈帧清除当前线程拥有的保护结构以及保护结构上挂的线程清除TCDCurrentThread将堆栈从线程堆栈切换到系统堆栈然后将控制权交给TCDSchedule重新进行调度从而引起TASK或HISR的竞争(线程的抢占)。如果没有其它线程在等待该保护结构则直接清除当前线程的tccurrentprotect以及保护结构的tctcbpointer然后返回。TCDUnprotectSpecific首先清除指定保护结构上挂的线程tctcbpointer如果没有其它线程在等待该保护结构就直接返回。如果还有其它线程在等待该保护结构则为当前线程建立一个Solicited类型的堆栈帧清除当前线程TCDCurrentThread将堆栈从线程堆栈切换到系统堆栈然后将控制权交给TCDSchedule重新进行调度从而引起TASK或HISR的竞争(线程的抢占)。、保护的调度(TCTScheduleProtected)保护结构的调度也就是调度拥有保护结构的线程直至其释放保护。TCTScheduleProtected首先为想得到保护结构的当前线程建立一个Solicited类型的堆栈帧将拥有保护结构的线程置为当前线程TCDCurrentThread将堆栈切换至系统堆栈将控制权交给TCTControlToThread这里将恢复运行因线程抢占而挂起的拥有保护结构的线程等其处理完相关数据结构后就会调用TCDUnprotect来释放保护结构释放时就会发现还有另一个线程在等待该保护结构然后控制权就会交给TCDSchedule重新进行调度引起线程之间的竞争。注意:TCTScheduleProtected并没有修改TCDExecuteTask及TCDExecuteHISR只是临时地将拥有保护结构的线程置为当前线程当拥有保护结构的线程释放保护后TCDSchedule又会恢复运行想得到保护结构的TCDExecuteTask或TCDExecuteHISR。数据结构保护的处理示意图如下:Nucleus操作系统不具备优先级自动逆转的功能它有自己的解决方法。当高优先级的线程不能获得临界系统资源时高优先级的任务并没有挂起所有在此优先级之下的线程就都不能运行Nucleus会把拥有临界系统资源的低优先级线程临时地升为当前线程TCDCurrentThread(相当于临时把优先级升为最高)当低优先级的线程释放临界系统资源后高优先级的任务马上会恢复运行。VxWorks是通过互斥信号灯来实现优先级自动逆转的操作系统能自动提升获得此信号灯的所有任务的优先级与获得此信号灯的最高优先级任务相同。如果所有任务没有设置信号灯选项它们的工作方式是这样的:优先级低的任务获得一个临界资源且正在运行高优先级任务因为没有获得这一临界资源而挂起这时一个中优先级任务就绪如果低优先级的任务是可以抢占的它就会抢占低优先级的任务造成高优先级任务没有机会运行。如果低优先级和高优先级的任务都设置了互斥信号灯操作系统会自动提高低优先级任务的优先级和高优先级任务相同这样就不会出现上面的问题。​ 任务间通信Nucleus任务间的通信机制有邮箱(mailboxes)消息对列(queues)消息管道(pipes)。它们之间的主要不同之处在于使用的消息结构不同。Nucleus的邮箱(mailboxes)提供的是单个消息的简单通信机制。每个邮箱最多只能容纳一条个长字(bytes)的消息。邮箱可以动态地创建和删除邮箱的数目不受限制。Nucleus的队列(queues)提供的是传送多个消息的通信机制一条消息由一个以上的长字组成支持定长和变长的消息类型。队列也可以动态地创建和删除队列的数目不受限制。Nucleus的管道(pipes)提供的也是传送多个消息的通信机制一条消息由由一个以上的字节组成支持定长和变长的消息类型。管道也可以动态地创建和删除管道的数目不受限制。Nucleus的队列和管道是十分类似的不同之处在于:队列的消息按长字来访问管道的消息按字节来访问。下面我们只拿管道来分析Nucleus的任务通信机制。​ 消息管道(Pipes)管道支持定长和变长的消息类型消息格式的类型在创建时就已经固定。定长消息的管道比较好管理每条消息都具有固定的长度变长消息的管道管理起来就相对复杂一点每条消息前还要加四个bytes用来指示消息的长度一条变长的消息有时不是位于连续的内存空间也就是当管道写指针指到管道尾时一条变长消息就会分成管道的首尾两块。、管道的创建(PICCreatePipe)管道创建时首先要对以下比较重要的管道控制块数据成员赋初值用来确定管道的大小和消息类型:pipe>pififosuspend:NUFIFO表示任务在管道上的挂起顺序是按先入先出(FIFO)的顺序NUPRIORITY表示任务在管道上的挂起顺序是按照优先级的顺序。pipe>pifixedsize:NUVARIABLESIZE表示管道的消息类型是变长的NUFIXEDSIZE表示管道的消息类型是定长的。pipe>pimessagesize:对于定长的消息类型表示每条消息的大小对于变长类型的消息表示最长消息的大小。pipe>pipipesize:管道总的大小。pipe>pistartpipe>piend:管道在内存空间的起始和终止地址指针。pipe>pireadpipe>piwrite:管道的读写指针。pipe>pisuspendlist:管道上的挂起任务链表头指针pipe>piurgentlist:管道上发送紧急消息的挂起任务链表头指针。pipe>pitaskswaiting:管道上等待任务的个数。注意:只有任务才能挂起在管道上HISR是不能挂起的HISR在管道上收发消息的时候都要使用NUNOSUSPEND选项。然后将该管道插入已创建管道链表PIDCreatedPipesList。、管道的删除(PICDeletePipe)管道的删除首先要把该管道从已创建管道链表中删除。其次将管道挂起任务链表pipe>pisuspendlist上被挂起的任务(在管道上收发消息时被挂起)全部恢复(TCCResumeTask)给所有的任务返回状态NUPIPEDELETED。然后将管道紧急消息挂起链表pipe>piurgentlist上被挂起的任务(做PISSendToFrontOfPipe调用时被挂起)全部恢复给所有任务返回状态NUPIPEDELETED。如果被恢复的任务优先级比做管道删除系统调用的任务优先级高且做该系统调用的任务可以抢占则控制权会交给TCTControlToSystem发生任务的抢占。、往管道发送消息(PICSendToPipe)PICSendToPipe负责往指定的管道发送消息消息的长度由用户指定。如果管道上有一个或更多的任务在等待消息则发送的消息直接拷贝至第一个等待任务的消息域同时挂起的任务被恢复如果被恢复的任务优先级比当前任务的优先级高且当前任务可以抢占则控制权会交给TCTControlToSystem发生任务的抢占。如果管道上没有足够的空间来存放消息则往管道发送消息的任务就会被挂起(可以选择挂起或不挂起)。如果管道上有空间存放消息则要发送的消息就会copy至管道中。往管道发送消息的处理流程如下:、从管道中接收消息(PICReceiveFromPipe)PICReceiveFromPipe负责从指定的管道中接收消息接收到的消息的实际长度放在actualsize中。如果管道中没有消息任务可以选择挂起与否。如果有任务做PISSendToFrontOfPipe系统调用发送紧急消息到管道中由于管道中没有空间而被挂起则紧急消息会copy至接收任务的接收消息域被挂起的任务马上会恢复如果当前任务可以抢占且被挂起的任务比当前任务优先级高则控制权会交给TCTControlToSystem发生任务抢占。如果管道中有消息则管道中读指针指向的消息会copy至接收任务的接收消息域。如果有发送消息的任务挂起在管道上且管道中又有空间存放其要发送的消息则被挂起的任务会恢复其要发送的消息会copy至管道中。如果当前任务可以抢占且被挂起的任务比当前任务优先级高则控制权会交给TCTControlToSystem发生任务抢占。从管道接收消息的处理流程如下:拿两个任务举列来说明消息管道的收发过程(假设没有其它任务在运行):、两个任务具有不同的优先级高优先级任务不停地发送低优先级任务不停地接收。、两个任务具有不同的优先级低优先级任务不停地发送高优先级任务不停地接收。、两个任务具有相同的优先级分时间片运行。​ 任务的同步Nucleus提供:信号量(semaphores)、事件组(eventgroups)和信号(signals)来实现任务的同步。其中信号量和事件组是公共的工具和任务及其他操作系统对象的联系由用户来定而信号只和特定的任务相关。下面分别说明。​ 信号量(semaphores)信号量提供一种机制来控制操作系统临界资源的分配。两个基本操作是obtain和releaseobtain为减少信号量release为增加信号量。信号量最常见的应用就是资源的分配一个任务获得某一资源信号量就减少任务释放资源信号量就增加。当一个任务试图去obtain一个为的信号量时任务就会挂起直到此信号量被释放任务才恢复。当多个任务试图obtain一个信号量时任务就会根据信号量创建时支持的是FIFO挂起方式还是priority挂起方式来决定任务挂起的顺序。Nucleus避免死锁的方法是当应用程序使用信号量时强加一些规则给应用程序如禁止任务在同一时间占用多个信号量等。当一个低优先级的任务拥有高优先级的任务所需要的信号量时就会发生优先级逆转以避免任务被无限期地挂起。Nucleus可以动态地创建和删除信号量信号量创建时的初始值也可以是到(xfffffffe)的任意值。​ 事件组(eventgroups)事件组提供一种机制来指示一个特定的系统事件。每个事件组由bit组成每个bit叫事件标志代表一个事件。事件标志位可以用逻辑与或的组合来设置或清除当一个任务试图得到事件标志位未置位的事件时任务将挂起置位后任务恢复。Nucleus可以动态地创建和删除事件组。​ 信号(signals)信号和事件组类似但是在操作上有明显的区别。事件组是用来同步的在指定的服务请求执行之前任务不会识别存在的事件标志。信号以异步的方式来操作当一个信号出现任务就中断执行转入执行由任务预先定义的信号处理程序(signalhandlingroutine)。每个任务可以处理个信号每个信号由bit来表示。信号处理程序类似于高级中断服务程序它不会被新的信号中断。所有新信号的处理都要在当前的信号处理完成之后。任务创建时所有信号都是disable的个别的信号可以由任务动态地enable和disable。当信号处理程序被唤醒时信号就被清除。​ 定时器Nucleus使用了一个硬件定时中断来作为系统定时器硬件定时的长度可由用户调整并且作为操作系统的一个tick也就是一个时间片。用户可以利用系统定时器通过系统调用NUCreateTimer()来创建自己的定时器这个系统调用必须指定时超处理的入口函数以及定时的长度(时间片数)。Nucleus的每个任务内部都具有一个builtin定时器用来进行相同优先级的时间片调度。系统定时器使用的是SIM模块的周期中断定时器对于不同的目标板由于用的时钟源不同PIT可以定时的长度也是不同的这里我们使用的是MS的PIT中断也就是每个时间片(tick)是MS。硬件定时器的中断是一个非管理的中断其入口函数是TMTTimerInterrup。TMTTimerInterrup首先将系统时钟TMDSystemClock加然后判断系统定时器TMDTimer及任务时间片定时器TMDTimeSlice是否溢出如果这两个定时器有一个时超则会进行中断上下文保护、恢复同时激活TMDHISR来处理时超事件这时会产生线程抢占。如果定时中断产生后没有定时器溢出则会调用TCTCheckForPreemption进行抢占判

用户评论(0)

0/200

精彩专题

上传我的资料

每篇奖励 +2积分

资料评价:

/33
1下载券 下载 加入VIP, 送下载券

意见
反馈

立即扫码关注

爱问共享资料微信公众号

返回
顶部