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

Nucleus实时操作系统分析报告

举报
开通vip

Nucleus实时操作系统分析报告Nucleus实时操作系统分析报告 Nucleus实时操作系统分析报告 目 录 TOC \o 一、 Nucleus的内核(Kernel) 3 1.1 系统启动 3 1.2 初始化线程 4 1.3 线程调度 4 1.3.1 任务的调度 5 1.3.2 中断的调度 9 1.3.3 操作系统数据结构的保护 14 1.4 任务间通信 16 1.4.1 消息管道(Pipes) 17 1.5 任务的同步 21 1.6 定时器 21 1.7 内存管理 22 1.8 输入/输出设备驱动 23 Nucleus内核总结: 23 二、 ...

Nucleus实时操作系统分析报告
Nucleus实时操作系统 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 报告 软件系统测试报告下载sgs报告如何下载关于路面塌陷情况报告535n,sgs报告怎么下载竣工报告下载 Nucleus实时操作系统分析报告 目 录 TOC \o 一、 Nucleus的内核(Kernel) 3 1.1 系统启动 3 1.2 初始化线程 4 1.3 线程调度 4 1.3.1 任务的调度 5 1.3.2 中断的调度 9 1.3.3 操作系统数据结构的保护 14 1.4 任务间通信 16 1.4.1 消息管道(Pipes) 17 1.5 任务的同步 21 1.6 定时器 21 1.7 内存管理 22 1.8 输入/输出设备驱动 23 Nucleus内核总结: 23 二、 Nucleus的开发工具 25 2.1 Nucleus C++ 25 2.2 Nucleus MNT 25 2.3 Nucleus VNET 25 2.4 Nucleus PC+ 25 2.5 Nucleus FILE 25 2.6 Nucleus CLIB 26 2.7 Nucleus GRAFIX 26 2.8 Nucleus EDE 26 2.9 Nucleus UDB 27 2.10 Nucleus DEBUG+ 27 2.11 第三方产品的支持 29 2.12 SDS SingleStep 29 三、 Nucleus的网络支持 31 3.1 Internet套件 31 Nucleus NET 31 Nucleus SNMP 31 Nucleus RMON 32 Nucleus SPAN 32 3.2 Nucleus WebServ 32 3.3 Nucleus Jvi 32 3.4 Nucleus EPILOGUE 32 Nucleus实时操作系统分析报告 Nucleus实时操作系统是Accelerater Technology公司开发的嵌入式RTOS产品,只需一次性购买Licenses,就可以获得操作系统的源码。 Nucleus购买的灵活性比较大:Kernel,Networking,File System,Web Technology,Target Debugger可以分开购买,如果我们只需要微内核的话只要购买Kernal和Debugger,当前的下位机就是这样配置的。但是,如果以后我们要开发接入服务器和IP Phone的话就必须购买一堆网络协议;上位机要用的话还要购买文件系统。VxWorks就不是这样,其OS基本部分中已经包括了很多基本的网络协议,只有一些比较特殊的网络协议需要单独购买,比如:SNMP1/2,OSPF2,IPX等。当然,Nucleus的好处在于各层协议都提供的是源码,Vxworks则不提供。 Nucleus的另一大好处是程序员不用写板支持软件包(BSP),因为操作系统已经开放给程序员,不同的目标板在操作系统BOOT时可以通过修改源码进行不同的配置。对于程序员来说,写BSP是一项比较繁琐的任务,有了OS的源码这项工作就简单多了,同时调试时也可以跟踪到中断、寄存器那一级,简化了硬件的调试。VxWorks和pSOS都必须购买 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 的BSP模板工具来写板支持软件包,开发工作量就比较大了。 Nucleus对CPU的支持能力比较强,支持当前流行的大多数RISC、CISC、DSP处理器,比如:80x86(实时、保护模式)、68xxx、PowerPC、i960、MIPS、SH、ARM、ColdFire。Nucleus系统对于不同的处理器,操作系统的源码大部分是相同的,只有5%的源码是用汇编写的跟CPU有关。如果使用另外的CPU,则只用修改5、6个汇编程序就可以进行移植,但是对于不同系列的处理器,它们的Compiler、Linker、Debugger是不同的(要另外购买),而且编译预处理的过程也是不一样的,这就要投入一部分力量去进行操作系统的移植。当然,这也是Nucleus的一个优点,VxWorks和pSOS都没有源码,换一个系列的CPU操作系统又要重新购买,实际上他们OS开发的工作量并没有多少,但却造成了用户的重复性投资。 当然,Nucleus作为一个非主流的操作系统,其自身也存在着很多不足之处,比如:实时性不够、定时中断管理不可靠、I/O管理太简单、操作系统的调试工具太少等。下面分别说明。 1、​ Nucleus的内核(Kernel) Nucleus的核心是一个实时的多任务内核——Nucleus PLUS,具有以下特性(Accelerater Technology公司宣称的): 可移植性: Nucleus PLUS可用于大多数流行的处理器。因为Nucleus PLUS主要是用标准C写的,移植到新的处理器系列相对很直接。这也就意味着用Nucleus PLUS开发的应用程序也具有很高的可移植性。 可用性: 不像其他的商业内核,Nucleus PLUS的系统调用名直接表明了它的功能。比如,你可以通过Nu_Create_Task系统调用来创建一个任务。Nucleus PLUS的系统调用都 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 成具有类似的入口参数和返回值类型。Nucleus PLUS的对象都不隐式地和别的对象相关。比如,邮箱和任务之间是不相关的。因此,用户可以利用多个Nucleus PLUS对象之间的结合形成混合系统调用。 配置: Nucleus PLUS最终是以C库的形式提供给用户,你可以选择所需要的部分链入到你的应用程序中。但是,其可裁剪性并不好,如果某些系统功能不要的话,必须在Nucleus.h的头文件中加一些宏定义,比如定义:#define NU_ENABLE_STACK_CHECK,那么每个系统调用时都要进行堆栈检查,否则就不进行堆栈检查,可以删除的功能并不多。VxWorks的可裁剪性就强多了,有300多个独立模块,微内核6K,最小系统<30K,配全了可达到几百K。 特性: ①、快速响应时间:对临界资源的检测时间不依赖于占有该临界资源的线程执行时间的长短,一旦低优先级线程释放掉临界资源(不管其是否执行完),高优先级线程就会抢占运行。 ②、每个任务的执行时间和其他任务的处理时间无关。 ③、较高吞吐量:随着任务数目的增多,任务的调度时间为常数。 ④、可扩展性:利用现有系统调用的结合可得到新的系统调用。 Nucleus PLUS提供其他实时内核都具有的系统服务,比如:任务控制、任务通信、任务同步、内存管理、可编程的定时器、标准的输入/输出设备接口等。对任务的调度依赖优先级、时间片的方法, 所有的操作系统对象(任务、邮箱、队列、管道等)都可以动态地创建和删除。创建一个对象时,要指定指定其控制块的内存区域和其它的数据要求(堆栈空间等)。 Nucleus在任务调度时,首先查看任务是否可以抢占,如果不能抢占,则一直执行到任务完成或任务放弃时间片;否则,依靠优先级进行调度,先调入优先级最高的任务,对于优先级相同的任务则分享时间片、轮流调度。 1.1​ 系统启动 对于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,同时也是系统的主函数,具有另外的标号名main。 系统初始化首先完成硬件寄存器的配置,包括:存储器片选(CS0~CS7),软件看门狗SWT,系统周期定时器PIT,A口、B口、C口引脚功能设定,串行通信控制器的初步配置等。这些硬件的配置跟目标板有关,需要用户自己用汇编来写。 其次,将系统堆栈指针TCD_System_Stack初始化为堆栈区stack的顶部,同时在系统内存区sys_memory中拿出TMD_HISR_Stack_Size大小的一片内存用作高级中断服务程序的堆栈HISR_STACK。 然后,控制交给INC_Initialize。INC_Initialize首先完成操作系统数据结构的初始化,包括:线程控制、邮箱、队列、管道、信号量、事件、分区内存、动态内存、定时器、I/O Driver等。其次,调用Application_Initialize,这一函数由用户编写,完成任务、消息队列等的创建、中断的注册以及应用程序的初始化。当所有的初始化都完成后,INC_Initialize调用TCT_Schedule开始线程的调度。 1.3​ 线程调度 TCT_Schedule是线程调度的入口,负责将控制权交给具有最高优先级的高级中断服务程序HISR(TCD_Execute_HISR)或处于就绪状态的最高优先级任务(TCD_Execute_Task)。当没有任务或HISR执行时,线程调度就在TCT_Schedule中做死循环,等待TASK或HISR就绪。HISR的优先权比任务高,一旦有HISR就绪,则当前调度的任务将会被挂起,优先调度HISR。 一旦有TASK或HISR就绪,控制就会交给TCT_Control_To_Thread,在这里将TCD_Execute_HISR或TCD_Execute_Task设置为当前线程TCD_Current_Thread,启动任务时间片定时器,根据线程的不同堆栈类型恢复堆栈,然后执行RTS或RTE指令,将控制权交给线程。 Nucleus的线程有两种类型的堆栈帧: 任务创建时,要建立一个初始堆栈帧,线程入口是TCC_Task_Shell,该Shell执行任务的入口程序,通常任务的执行是一个死循环,不停地在等待消息或事件,如果没有消息或事件任务就会挂起,否则往下执行。如果,任务在Shell调度中返回,则表示该任务已执行完毕,将任务终止,其状态置为NU_FINISHED。 HISR创建时,也要建立一个初始堆栈帧,线程入口是TCC_HISR_Shell。HISR_Shell调度的是当前具有最高优先级的HISR,直至TCD_Execute_HISR的激活计数器tc_activation_count为0,才调度同一优先级或低优先级的其它HISR。HISR_Shell对HISR调度就是执行HISR的入口程序,然后根据激活计数器循环调度,HISR是不能被挂起的。 1.3.1 任务的调度 任务的调度需要用到以下比较重要的数据结构: TCD_Created_Tasks_List:已创建任务链表的头指针。 TCD_Priority_List[256]:任务控制块TCB的指针数组,每个元素是优先级0~255就绪任务链表的头指针。 TCD_Priority_Groups:按位来定义的长字,对应32组优先级,每bit是一组,负责8个优先级,如果其中任意一个优先级有任务就绪,则该bit置1。 TCD_Sub_Priority_Groups[32]:子优先级组的位映像图,每一元素对应一组优先级。比如TCD_Sub_Priority_Groups[0]对应优先级0~7,bit0~bit7分别表示优先级0~7。 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:任务已就绪,但是有其它任务在运行。 Suspended:任务在等待请求服务完成的过程中被挂起,一旦请求服务完成,任务就会迁移至ready状态。 Finished:该任务的处理已经完成(在TCC_Task_Shell调度中返回)。一旦任务处于这种状态就不能再执行了,除非任务被复位。 Terminated:任务被Killed。一旦任务处于这种状态就不能再执行了,除非任务被复位。 Nucleus的每个任务都具有一个0~255的优先级,0表示最高优先级,255表示最低优先级,对于相同优先级的任务分时间片运行,不同优先级的任务则可以发生抢占。 任务的调度涉及到:创建任务、删除任务、复位任务、终止任务、恢复任务、挂起任务等。 ①、创建任务(TCC_Create_Task) 任务的创建一般是在Application_Initialize中进行,当然也可以在其它任务中动态地创建和删除任务。任务创建的流程如下: ②、删除任务(TCC_Delete_Task) 对任务删除时,默认任务是处于finished或terminated状态。将任务删除,主要是将任务从已创建任务链表中删除,并不能释放与任务相关连的控制块(TCB)和堆栈。 ③、复位任务(TCC_Reset_Task) 当任务处于finished或terminated状态时,才能对该任务执行复位,否则返回NU_NOT_TERMINATED,表示任务没有结束或终止。任务复位主要是将任务控制块中的数据成员重新赋初值,重新创建任务堆栈(任务堆栈复位),将任务状态置为无条件挂起状态NU_PURE_SUSPEND。 ④、终止任务(TCC_Terminate_Task) 如果要终止的是当前任务(TCD_Current_Thread),则直接将任务挂起,将任务状态置为NU_TERMINATED。 如果要终止的不是当前任务,则要对任务状态进行判断。如果任务已经处于finished或terminated状态,则什么也不做。如果任务处于ready状态,则直接将任务挂起,将任务状态置为NU_TERMINATED。如果任务处于suspended状态,则必须释放和该任务相关的所有保护结构后才能将任务终止。 ⑤、恢复任务(TCC_Resume_Task) 如果任务可以获得执行所需要的系统资源,比如:对于做NU_Send_To_Pipe系统调用的任务如果消息管道已有空余空间,或者做NU_Reveive_From_Pipe系统调用的任务如果管道中有消息,那么挂起在该管道上的任务就会恢复。 如果任务挂起类型与请求恢复类型一致,则把任务状态置为NU_READY,将任务插入就绪任务优先级链表TCD_Priority_List[task->tc_priority],设置优先级组TCD_Priority_Groups和子优先级组TCD_Sub_Priority_Groups[task->tc_priority/8]中本任务优先级对应的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_Thread,如果不是挂起当前任务,则要释放任务的当前保护结构tc_current_protect。 其次,如果任务的状态为该任务优先级就绪任务链表TCD_Priority_List[]中只有这一个任务ready,则要清空该优先级就绪任务链表,同时要清除子优先级组TCD_Sub_Priority_Groups和优先级组TCD_Priority_Groups对应的bit。如果要挂起的任务具有最高优先级,则要根据优先组和子优先组重新搜索最高优先级,如果其他组中没有任务就绪,则TCD_Highest_Priority=255。然后根据最高优先级,重新调整TCD_Execute_Task,如果最高优先级为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类型的堆栈帧,线程入口是调用TCD_Control_To_System的下一条指令,任务恢复时从这条指令开始继续执行。TCD_Control_To_System随后又将控制交给TCT_Schedule,TCT_Schedule根据TCD_Execute_HISR或TCD_Execute_Task开始下一轮的任务调度,如果调度过程中发现挂起任务需要的系统资源可以满足,就会把任务恢复,按照优先级重新调度。 任务挂起流程如下: 1.3.2​ 中断的调度 Nucleus的中断分为管理的和非管理的中断。 管理的中断需要向操作系统注册该中断向量,中断产生后通过该中断向量注册的低级中断服务程序(LISR)来激活高级的中断服务程序(HISR)。LISR主要完成硬件中断的处理,及激活HISR。HISR的调度类似于任务,具有优先级,可以使用大多数Nucleus的系统调用。 非管理的中断,则不需要通过操作系统进行管理,直接将中断服务程序挂到中断向量表上,上下文的保存与恢复都要用户自己来做,该中断自己不能嵌套,最好不要被管理的中断再次中断否则会引起堆栈出错,而且非管理的中断不能使用绝大多数的Nucleus系统调用,因为它可能会破坏操作系统某些保护的数据结构(当有线程在运行时)。非管理的中断适用于那些比较频繁的中断,如果通过操作系统来管理这些中断的话,其上下文保存与恢复的时间就比较长,中断的实时性就不能满足要求。 非管理的中断比较简单,类似于以前我们写的中断服务程序,这里就不多说。下面我们讨论的中断的调度都指的是Nuclesu管理中断的调度。 中断的调度需要用到以下比较重要的数据结构: TCD_Registered_LISRs[256]:对应68360的256个中断向量。0表示该中断向量没有注册,是操作系统不能处理的中断(Unhandled_Interrupt);非0表示该中断向量已注册,且其值为在LISR函数指针数组TCD_LISR_Pointers中的索引下标。 TCD_LISR_Pointers[30]:LISR函数指针数组,指向当中断产生时要调用的低级中断服务程序LISR的入口函数。 TCD_Interrupt_Count:表示有多少个中断服务程序(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_Heads[3]:对应HISR优先级0~2,每个数组元素是该优先级已激活HISR链表的头指针。 TCD_Active_HISR_Tails[3]:对应HISR优先级0~2,每个数组元素是该优先级已激活HISR链表的尾指针。 TCD_Execute_HISR:当前正在执行或要执行的具有最高优先级的HISR指针。 每个HISR具有一个0~2的优先级,0表示最高优先级,2表示最低优先级,相同优先级的HISR按照先入先出的顺序处理,优先级不同的HISR按照优先级的高低进行调度。HISR是不能被挂起的,因此其所有的系统调用都要加上NU_NO_SUSPEND参数。 中断的调度包括:中断向量的注册、HISR的创建与删除、上下文的保护与恢复、LISR的执行、HISR的激活以及HISR的调度等。 ①、中断向量的注册(TCC_Register_LISR) 一个中断要通过操作系统管理起来,首先要将其中断向量通过系统调用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。 如果该中断向量没有注册过,则在LISR函数指针数组TCD_LISR_Pointers中找出一个没有使用的单元,将new_lisr填入该单元,同时将该单元的下标填入该中断向量在TCD_Registered_LISRs中对应的单元。另外,还要判断INT_Loaded_Flag标志,如果该标志为0,则要替换掉当前的中断向量表,否则不修改当前的中断向量表。 ②、HISR的创建(TCC_Create_HISR) HISR也就是中断产生后,要在LISR中激活的高级中断服务程序。 HISR的创建比任务创建简单,不用进行设置任务状态、恢复任务等操作,只需创建一个HISR控制块HCB,初始化HCB中的一些参数,为HISR创建一个Solicited类型的堆栈帧,将该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相关的中断也关掉。 ④、上下文的保护(TCT_Interrupt_Context_Save) 通常的中断服务程序对要用到的寄存器都要进行堆栈保护,Nucleus操作系统除了做这些外,还要对当前线程进行保护,使得高级中断服务程序HISR可以抢占任务,让HISR得到快速的响应。Nucleus在系统空闲(没有线程运行)时,中断堆栈使用的是系统堆栈TCD_System_Stack;如果有线程(任务或HISR)在运行,使用的是任务或HISR堆栈,上下文保护完成之后,则将堆栈切换到系统堆栈TCD_System_Stack。 上下文保护首先将所有中断屏蔽掉,等保护完成之后再将中断打开。其次判断中断计数器TCD_Interrupt_Count,如果本次中断是中断嵌套,则将TCD_Interrupt_Count加1,然后返回。如果本次中断不是中断嵌套,则判断当前有没有线程在运行,如果当前有线程在运行,则为当前线程建立一个Interrupt类型的堆栈帧,将当前的堆栈指针保存在线程控制块中,再将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回;如果当前没有线程运行,则直接将堆栈指针切换到系统堆栈顶部TCD_System_Stack,然后返回。 ⑤、LISR的执行(TCC_Dispatch_LISR) 中断上下文保护完成后,就要根据中断向量,在LISR指针数组TCD_LISR_Pointers中索引到本中断向量的LISR入口函数指针,然后执行LISR函数,通常LISR要做的只是处理硬件中断及激活HISR。LISR执行完毕,则将上下文恢复,将控制权交给TCT_ Shedule进行系统调度。 要注意的是,如果某个中断向量没有注册,则会产生系统错误,进入系统错误线程ERC_System_Error处理,这种错误是致命的错误,导致整个系统进入一个死循环。 ⑥、上下文的恢复(TCT_Interrupt_Context_Restore) 上下文恢复首先判断是不是中断嵌套,如果是中断嵌套,则将TCD_Interrupt_Count减1,将堆栈保护的寄存器恢复,然后利用RTE指令从中断返回。如果不是中断嵌套,则将当前线程TCD_Current_Thread清为0,将堆栈切换到系统堆栈顶部TCD_System_Stack,将控制交给TCT_Shedule进行线程的重新调度,在这里HISR会抢占任务优先运行。 对于没有嵌套的中断恢复,并没有执行RTE指令,从中断产生的指令往下执行,而是将控制交给TCT_Shedule进行重新调度,这类中断恢复可以分三种情况进行分析: i)、系统空闲(做TCD_Shedule死循环)时,产生了中断,则上下文恢复后,再次运行TCD_ Shedule,然后调度由LISR激活的HISR(TCD_Execute_HISR)。 ii)、中断产生时,有一个任务在运行。由于在上下文保护时,已经给当前任务建立了一个中断类型的堆栈帧,同时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)、中断产生时,有一个高级中断服务程序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接着被中断的部分继续执行。 ⑦、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_Execute_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_HISR_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_Heads[X]及TCD_Active_HISR_Tails[X]置为NU_NULL),然后从激活链表头指针数组TCD_Active_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,将堆栈切换到系统堆栈顶部TCD_System_Stack。然后将控制权交给TCT_Shedule重新调度,如果还有其它的HISR被激活,则重复上面的过程;否则进入任务的调度,或在TCT_Shedule死循环,等待HISR被激活或任务ready。 一个完整的中断处理流程如下: 1.3.3​ 操作系统数据结构的保护 由于Nucleus操作系统的线程是可以抢占的,高优先级的任务可以抢占低优先级的任务,HISR可以抢占任务,HISR之间也可以抢占。 如果某个低优先级的任务正在通过系统调用对操作系统的某个数据结构进行操作,比如:正在修改已创建任务链表TCD_Created_Tasks_List,或正在往某个消息管道Pipe中填消息(要修改消息管道的数据成员),这时发生了任务抢占,如果发生抢占的高优先级任务也要修改同一数据结构,就必须等待低优先级的任务完成修改数据结构的系统调用后,再让高优先级的任务运行,否则就会破坏操作系统的数据结构。 这一机制是通过操作系统结构保护(Protect)实现的。任务运行时如果要修改操作系统的数据结构,就要通过系统调用TCT_Protect (TC_PROTECT *protect) 把该数据结构的保护结构保护起来,当任务对该数据结构的操作结束就会通过系统调用TCT_Unprotect (void)或TCT_Unprotect_Specific (TC_PROTECT *protect) 来释放保护结构。 如果低优先级的任务没有释放保护结构之前,发生了任务抢占,高优先级的任务抢占了低优先级的任务,如果高优先级的任务也要修改同一系统数据结构,那么在做系统调用TCT_Protect时,就会发现该数据结构的保护结构已经被另一个线程拥有,当前的线程就会暂时被挂起,将控制交给拥有保护结构的线程,等待拥有该保护结构的线程释放掉保护结构后,也就是拥有保护结构的线程在做TCT_ Unprotect时,高优先级的任务才能真正地把控制权抢占过来。 Nucleus共有四种跟线程调度有关的比较重要的保护结构: TCD_List_Protect:已创建任务链表的保护结构; TCD_System_Protect:系统保护结构,用于任务调度; TCD_LISR_Protect:用于LISR的创建和删除; TCD_HISR_Protect:用于HISR的创建和删除; 保护结构的结构体是这样定义的: typedef struct TC_PROTECT_STRUCT { TC_TCB tc_tcb_poiter; /* 拥有保护结构的线程指针 */ UNSIGNED tc_thread_waiting; /* 有线程在等待该保护结构的标志 */ }TC_PROTECT; ①、数据结构的保护(TCT_Protect) 当线程在操作的数据结构不想因为线程的抢占而破坏时,就要申请对该数据结构的保护,比如:在创建HISR时,就要申请HISR链表的保护TCT_Protect(&TCD_HISR_Protect);在链表插入完成后,就要调用TCT_Unprotect( )释放当前线程拥有的保护结构。 另外,如果线程正在执行的系统调用不想因为线程的抢占而中断时,就要申请系统保护结构的保护TCT_Protect(&TCD_System_Protect),比如:任务的恢复、任务的挂起、消息管道的收发等,这些操作都要申请系统保护结构,当系统调用结束后,就要调用TCT_Unprotect( )释放当前线程拥有的保护结构。 TCT_Protect首先判断要保护的结构是否被其它线程拥有,如果没有其它线程拥有要保护的结构,则将当前线线程TCD_Current_Thread赋给protect->tc_tcb_pointer,表示当前线程要占用该保护结构,清除保护结构的线程等待标志,将保护结构的指针送给TCD_Current_Thread->tc_current_protect,表示当前线程拥有一个保护结构,然后返回。 如果其它线程拥有要保护的结构,则要调用TCT_Schedule_Protectd,直至其它线程释放掉该保护结构,然后重复上面的处理,表示当前线程拥有该保护结构。 ②、保护结构的释放(TCT_Unprotect) 当拥有保护结构的线程处理完系统调用后,就要释放被保护的结构。保护结构的释放有两个系统调用:TCD_Unprotect释放的是线程当前的保护结构tc_current_protect;TCD_Unprotect_Specific释放的是用户或操作系统指定的保护结构。 TCD_Unprotect首先判断当前线程是否拥有一个保护结构,再判断保护结构的线程等待标志,看看是否有其它线程在等待该保护结构,如果这些条件都成立,则将控制权交给TCT_Control_To_System。TCT_Control_To_System为当前线程创建一个Solicited类型的堆栈帧,清除当前线程拥有的保护结构以及保护结构上挂的线程,清除TCD_Current_Thread,将堆栈从线程堆栈切换到系统堆栈,然后将控制权交给TCD_Schedule重新进行调度,从而引起TASK或HISR的竞争(线程的抢占)。 如果没有其它线程在等待该保护结构,则直接清除当前线程的tc_current_protect,以及保护结构的tc_tcb_pointer,然后返回。 TCD_Unprotect_Specific首先清除指定保护结构上挂的线程tc_tcb_pointer,如果没有其它线程在等待该保护结构就直接返回。如果还有其它线程在等待该保护结构,则为当前线程建立一个Solicited类型的堆栈帧,清除当前线程TCD_Current_Thread,将堆栈从线程堆栈切换到系统堆栈,然后将控制权交给TCD_Schedule重新进行调度,从而引起TASK或HISR的竞争(线程的抢占)。 ③、保护的调度(TCT_Schedule_Protected) 保护结构的调度也就是调度拥有保护结构的线程,直至其释放保护。 TCT_Schedule_Protected首先为想得到保护结构的当前线程建立一个Solicited类型的堆栈帧,将拥有保护结构的线程置为当前线程TCD_Current_Thread,将堆栈切换至系统堆栈,将控制权交给TCT_Control_To_Thread,这里将恢复运行因线程抢占而挂起的拥有保护结构的线程,等其处理完相关数据结构后,就会调用TCD_Unprotect来释放保护结构,释放时就会发现还有另一个线程在等待该保护结构,然后控制权就会交给TCD_Schedule重新进行调度,引起线程之间的竞争。 注意:TCT_Schedule_Protected并没有修改TCD_Execute_Task及TCD_ Execute_HISR,只是临时地将拥有保护结构的线程置为当前线程,当拥有保护结构的线程释放保护后,TCD_Schedule又会恢复运行想得到保护结构的TCD_Execute_Task或TCD_ Execute_HISR。 数据结构保护的处理示意图如下: Nucleus操作系统不具备优先级自动逆转的功能,它有自己的解决方法。当高优先级的线程不能获得临界系统资源时,高优先级的任务并没有挂起,所有在此优先级之下的线程就都不能运行,Nucleus会把拥有临界系统资源的低优先级线程临时地升为当前线程TCD_Current_Thread(相当于临时把优先级升为最高),当低优先级的线程释放临界系统资源后,高优先级的任务马上会恢复运行。 VxWorks是通过互斥信号灯来实现优先级自动逆转的,操作系统能自动提升获得此信号灯的所有任务的优先级,与获得此信号灯的最高优先级任务相同。如果所有任务没有设置信号灯选项,它们的工作方式是这样的:优先级低的任务获得一个临界资源且正在运行,高优先级任务因为没有获得这一临界资源而挂起,这时一个中优先级任务就绪,如果低优先级的任务是可以抢占的,它就会抢占低优先级的任务,造成高优先级任务没有机会运行。如果低优先级和高优先级的任务都设置了互斥信号灯,操作系统会自动提高低优先级任务的优先级和高优先级任务相同,这样就不会出现上面的问题。 1.4​ 任务间通信 Nucleus任务间的通信机制有邮箱(mailboxes),消息对列(queues),消息管道(pipes)。它们之间的主要不同之处在于使用的消息结构不同。 Nucleus的邮箱(mailboxes)提供的是单个消息的简单通信机制。每个邮箱最多只能容纳一条4个长字(16 bytes)的消息。邮箱可以动态地创建和删除,邮箱的数目不受限制。 Nucleus的队列(queues)提供的是传送多个消息的通信机制,一条消息由一个以上的长字组成,支持定长和变长的消息类型。队列也可以动态地创建和删除,队列的数目不受限制。 Nucleus的管道(pipes)提供的也是传送多个消息的通信机制,一条消息由由一个以上的字节组成,支持定长和变长的消息类型。管道也可以动态地创建和删除,管道的数目不受限制。 Nucleus的队列和管道是十分类似的,不同之处在于:队列的消息按长字来访问,管道的消息按字节来访问。下面我们只拿管道来分析Nucleus的任务通信机制。 1.4.1​ 消息管道(Pipes) 管道支持定长和变长的消息类型,消息格式的类型在创建时就已经固定。定长消息的管道比较好管理,每条消息都具有固定的长度;变长消息的管道管理起来就相对复杂一点,每条消息前还要加四个bytes用来指示消息的长度,一条变长的消息有时不是位于连续的内存空间,也就是当管道写指针指到管道尾时,一条变长消息就会分成管道的首尾两块。 ①、管道的创建(PIC_Create_Pipe) 管道创建时,首先要对以下比较重要的管道控制块数据成员赋初值,用来确定管道的大小和消息类型: 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_end:管道在内存空间的起始和终止地址指针。 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) 管道的删除首先要把该管道从已创建管道链表中删除。 其次,将管道挂起任务链表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,发生任务的抢占。 ③、往管道发送消息(PIC_Send_To_Pipe) PIC_Send_To_Pipe负责往指定的管道发送消息,消息的长度由用户指定。 如果管道上有一个或更多的任务在等待消息,则发送的消息直接拷贝至第一个等待任务的消息域,同时挂起的任务被恢复,如果被恢复的任务优先级比当前任务的优先级高,且当前任务可以抢占,则控制权会交给TCT_Control_To_System,发生任务的抢占。 如果管道上没有足够的空间来存放消息,则往管道发送消息的任务就会被挂起(可以选择挂起或不挂起)。 如果管道上有空间存放消息,则要发送的消息就会copy至管道中。 往管道发送消息的处理流程如下: ④、从管道中接收消息(PIC_Receive_From_Pipe) PIC_Receive_From_Pipe负责从指定的管道中接收消息,接收到的消息的实际长度放在actual_size中。 如果管道中没有消息,任务可以选择挂起与否。 如果有任务做PIS_Send_To_Front_Of_Pipe系统调用发送紧急消息到管道中,由于管道中没有空间而被挂起,则紧急消息会copy至接收任务的接收消息域,被挂起的任务马上会恢复,如果当前任务可以抢占且被挂起的任务比当前任务优先级高,则控制权会交给TCT_Control_To_System,发生任务抢占。 如果管道中有消息,则管道中读指针指向的消息会copy 至接收任务的接收消息域.。如果有发送消息的任务挂起在管道上,且管道中又有空间存放其要发送的消息,则被挂起的任务会恢复,其要发送的消息会copy至管道中。如果当前任务可以抢占且被挂起的任务比当前任务优先级高,则控制权会交给TCT_Control_To_System,发生任务抢占。 从管道接收消息的处理流程如下: 拿两个任务举列来说明消息管道的收发过程(假设没有其它任务在运行): ①、两个任务具有不同的优先级,高优先级任务不停地发送,低优先级任务不停地接收。 ②、两个任务具有不同的优先级,低优先级任务不停地发送,高优先级任务不停地接收。 ③、两个任务具有相同的优先级,分时间片运行。 1.5​ 任务的同步 Nucleus提供:信号量(semaphores)、事件组(event groups)和信号(signals)来实现任务的同步。其中信号量和事件组是公共的工具,和任务及其他操作系统对象的联系由用户来定;而信号只和特定的任务相关。下面分别说明。 1​ 信号量(semaphores) 信号量提供一种机制来控制操作系统临界资源的分配。两个基本操作是obtain和release,obtain为减少信号量,release为增加信号量。信号量最常见的应用就是资源的分配,一个任务获得某一资源,信号量就减少,任务释放资源,信号量就增加。当一个任务试图去obtain一个为0的信号量时,任务就会挂起,直到此信号量被释放,任务才恢复。 当多个任务试图obtain一个信号量时,任务就会根据信号量创建时支持的是FIFO挂起方式还是priority挂起方式来决定任务挂起的顺序。 Nucleus避免死锁的方法是当应用程序使用信号量时强加一些规则给应用程序,如禁止任务在同一时间占用多个信号量等。 当一个低优先级的任务拥有高优先级的任务所需要的信号量时,就会发生优先级逆转,以避免任务被无限期地挂起。 Nucleus可以动态地创建和删除信号量,信号量创建时的初始值也可以是0到4,294,967,294(0xfffffffe)的任意值。 2​ 事件组(event groups) 事件组提供一种机制来指示一个特定的系统事件。每个事件组由32bit组成,每个bit叫事件标志,代表一个事件。事件标志位可以用逻辑与/或的组合来设置或清除, 当一个任务试图得到事件标志位未置位的事件时,任务将挂起;置位后,任务恢复。 Nucleus可以动态地创建和删除事件组。 3​ 信号(signals) 信号和事件组类似,但是在操作上有明显的区别。事件组是用来同步的,在指定的服务请求执行之前,任务不会识别存在的事件标志。信号以异步的方式来操作,当一个信号出现,任务就中断执行,转入执行由任务预先定义的信号处理程序(signal_handling routine)。每个任务可以处理32个信号,每个信号由1bit来表示。 信号处理程序类似于高级中断服务程序,它不会被新的信号中断。所有新信号的处理都要在当前的信号处理完成之后。 任务创建时所有信号都是disable的,个别的信号可以由任务动态地enable和disable。 当信号处理程序被唤醒时,信号就被清除。 1.6​ 定时器 Nucleus使用了一个硬件定时中断来作为系统定时器,硬件定时的长度可由用户调整,并且作为操作系统的一个tick,也就是一个时间片。 用户可以利用系统定时器,通过系统调用NU_Create_Timer( )来创建自己的定时器,这个系统调用必须指定时超处理的入口函数,以及定时的长度(时间片数)。 Nucleus的每个任务内部都具有一个built_in定时器,用来进行相同优先级的时间片调度。 系统定时器使用的是68360 SIM60模块的周期中断定时器,对于不同的目标板,由于用的时钟源不同,PIT可以定时的长度也是不同的,这里我们使用的是1MS的PIT中断,也就是每个时间片(tick)是1MS。 硬件定时器的中断是一个非管理的中断,其入口函数是TMT_Timer_Interrup。 TMT_Timer_Interrup首先将系统时钟TMD_System_Clock加1,然后判断系统定时器TMD_Timer及任务时间片定时器TMD_Time_Slice是否溢出,如果这两个定时器有一个时超,则会进行中断上下文保护、恢复,同时激活TMD_HISR来处理时超事件,这时会产生线程抢占。 如果定时中断产生后,没有定时器溢出,则会调用TCT_Check_For_Preemption进行抢占判
本文档为【Nucleus实时操作系统分析报告】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_608288
暂无简介~
格式:doc
大小:382KB
软件:Word
页数:33
分类:互联网
上传时间:2010-12-18
浏览量:18