一种Windows系统实时扩展的两级调度方法
【专利摘要】本发明涉及一种Windows系统实时扩展的两级调度方法,利用Windows系统亲缘性设定,对系统CPU资源进行重新分配,指定实时任务运行核心,以保证实时任务所需资源;设置IOAPIC重定向表,使外部中断指向特定内核处理,不会影响实时内核的运行;同时,利用Local?APIC的时钟中断计数器,为任务调度提供高精度时钟信号;再利用WinDbg从NTDLL中获取Windows进程、线程操作函数入口地址,封装成DLL,形成任务控制的基本接口;对内核任务控制块和任务队列进行锁定与映射,使得调度算法的编写与调试可以在用户态进行,最后挂入Local?APIC高精度时钟的中断处理例程中,完成实时内核调度算法。
【专利说明】—种Windows系统实时扩展的两级调度方法
【技术领域】
[0001]本发明属于计算机应用【技术领域】,涉及一种Windows系统实时扩展的两级调度方法。
【背景技术】
[0002]随着嵌入式实时技术以及虚拟试验技术的飞速发展,在工业领域中和仿真试验中Windows操作系统也越来越多的被用作实时应用的系统平台。为了满足实时性的要求,增加Windows系统本身的实时能力非常必要。现有的实时操作系统专利,多是有关实时操作系统理论有关的创新如调度、资源管理等,基本没有关于Windows平台对应实时方案。现有Windows实时方案均为商业方案如:Windows RTX、INtime。他们都是以接管HAL (硬件抽象层)的方式提供一套与Windows本身系统并行的体系。虽然他们解决了 Windows本身实时能力不足的问题,但改造工作成本很高,一些常用驱动需要重新改写,系统灵活性降低。其商业性质,更是导致用户的使用成本大大增加,且不易定制。国内有专利《基于Windows驱动程序的实时系统》利用外部接口卡提供高精度定时,通过直接操作CPU寄存器完成实时任务切换。虽然基本满足了实时要求,但是直接操作寄存器有较高风险,且主要是面向嵌入式实时加工。而本发明则主要利用Windows本身调度的任务切换,提供一种线程可控的两级调度技术,从而提供实时基础。在此基础上可以方便的扩充或自定义实时调度方案,完成实时的用户程序的编写。
【发明内容】
[0003]要解决的技术问题
[0004]为了避免现有技术的不足之处,本发明提出一种Windows系统实时扩展的两级调度方法,主要解决了现有技术方法中Windows系统中任务的切换代价大和定时精度低问题。
[0005]技术方案
[0006]—种Windows系统实时扩展的两级调度方法,其特征在于步骤如下:
[0007]步骤1:利用Windows系统中进程和线程CPU亲缘性标志位,对系统资源进行重新分配,把系统中所有进程和线程都指向非实时内核,空出运行实时任务的CPU核心,为实时任务提供足够的运行资源;
[0008]步骤2:同时操作接口封装、中断管理以及内存锁定与映射这三个部分来实现实时任务的运行:
[0009]a)借助于微软公司提供的系统服务描述符表SSDT,利用已知的Windows接口特征码,搜索系统内存指令,获取进程和线程的创建、挂起、恢复、终止四个基本操作的函数入口地址,并根据系统函数原型,利用嵌入汇编进行调用,完成特定操作接口的封装;
[0010]b)使用IOAPIC可编程中断控制器编程把外部中断分派给各个非实时内核,避免外部中断对实时内核的干扰,通过设定实时内核内的初始计数寄存器上的计数值,产生10-100微秒的定时信号,为实时调度程序提供调度信号;
[0011]c)在内核态下预先申请连续的物理页面,对其进行锁定,防止该页面的换入换出,利用MDL机制,对该物理页面进行用户态映射,使用户态和内核态能够操作同一块物理空间,避免运行模式切换时造成的数据拷贝,将线程控制块TCB和进程控制块PCB映射为用户可用地址,在用户态下进行调度算法的编写与调试;
[0012]步骤3:系统在执行具体任务时,根据不同的任务类型,采用不同的处理内核调度策略,普通任务、Windows系统任务和外部中断使用非实时内核处理,采用Window系统自身的调度策略;实时任务、指定的特殊中断将会被实时内核模块处理,采用实时调度机制,从而实现Windows实时扩展的两级调度。
[0013]所述步骤2中设定初始计数寄存器上的计数值的步骤如下:
[0014]步骤a:获取CPU出厂的额定主频和原始外频,通过这两个值计算出倍频,三者之间的关系为:主频等于外频乘上倍频;
[0015]步骤b:查询注册表或利用rdtsc汇编指令计算得出CPU当前真实主频,根据步骤a计算的倍频,反算得出真实的外频,得到系统有效的总线频率;
[0016]步骤c:初始计数寄存器所填充的数值即为:调度周期/总线频率。
[0017]所述步骤3中实时调度机制的步骤如下:
[0018]步骤a:按照线程固有优先级将线程TCB插入READY_LIST ;
[0019]步骤b:根据线程运行结果将状态改变的线程TCB直接变换链表;
[0020]步骤c:利用软中断陷入内核进行任务切换,在调度时首先需要将DELAY_LIST中开始时间已到的线程插入READY_LIST中;
[0021]步骤d:对当前调度情形做判断,查看最高优先级任务是否改变:若没有改变则直接结束时钟中断处理,任务接着运行;若有改变则进入DPC例程进行相关调度,选出READY_LIST中优先级最高且状态为READY的线程,恢复其运行。
[0022]有益效果
[0023]本发明提出的一种Windows系统实时扩展的两级调度方法,解决了 Windows任务切换和定时实时性不足的问题,采用两级调度的策略进行了 Windows的实时扩展,调度周期稳定且支持随时的抢占切换,调度满足实时任务要求。利用DLL提供的相关接口,用户在编写应用时可以方便的进行自定义的调度策略或调度顺序。
[0024]本发明有利于用户程序的调试开发。由于没有更改和接管硬件抽象层HAL,所以实时任务可以不用改写一系列的驱动程序,而实时任务直接就是Win32应用程序。只需要加载驱动和DLL,利用现有的IDE工具就可方便的进行编写调试,减轻了实时应用开发难度。最大限度的利用Windows系统本身使得系统具有良好的稳定性。
【专利附图】
【附图说明】
[0025]图1Windows系统实时扩展的两级调度方法流程图
[0026]图2亲缘性设定图
[0027]图3Windows系统进程线程关系图
[0028]图4内核操作接口寻址过程
[0029]图5外部中断管理原理图[0030]图6计数值设定流程图
[0031]图7MDL结构示意图
[0032]图8用户态和内核态共享内存映射结构图
[0033]图9实时调度机制流程图
[0034]图10示例运行结果
【具体实施方式】
[0035]现结合实施例、附图对本发明作进一步描述:
[0036]本发明利用Windows驱动方式,把亲缘性设定、中断管理、内存锁定与映射和任务切换操作嵌入到系统内核中,使之成为一个独立运行的模块,完成实时扩展的两级调度,其流程图如图1所不。
[0037]具体实施步骤为:
[0038]步骤1:利用Windows系统中进程和线程的CPU亲缘性标志位,对系统资源进行重新分配。把系统中所有的进程和线程指向非实时内核,空出运行实时任务的CPU核心,为实时任务提供足够的运行资源,如图2所示。
[0039]亲缘性设定主要是通过对进程和线程的控制块进行操作,找到其亲缘性域,设置相应的亲缘性掩码,从而指定该进程或线程的运行内核。在Windows系统中,如图3所示,所有的进程控制块的ActiveProcessLinks活动进程链表域都是以双向链表的形式关联的,因此通过任意一个进程可以便利到所有的进程控制块。在每个进程控制块中,Affinity字段是该进程的亲缘性设置域,ThreadListHead线程链表域是该进程所有线程的链表头,通过这个标志位可以便利该进程下所有的线程。每个线程控制块的Affinity字段是该进程的亲缘性设置域。因此,通过进程与线程的层次关系可以对整个系统任务进行亲缘性设置。
[0040]各个操作系统版本中进程线程相应标志位偏移量不完全相同不同,可以在WinDbg中利用dt命令得到各个标志位的偏移量。在Windows xp SP3系统环境下,进程控制块中ThreadListHead 字段偏移量为 0x50, Affinity 字段偏移量为 0x5C, ActiveProcessLinks字段偏移量为0x88 ;线程控制块中ListEntry链表指针域字段偏移量为OxlBO,Affinity字段偏移量为0x124。把Affinity字段设置成相应的内核掩码即完成系统任务的亲缘性设置,如设置成ΟχΟΕ,表示该任务能够在I号、2号和3号内核上执行,不能在O号内核上执行。
[0041]步骤2:通过亲缘性设定,保证了实时任务运行的硬件资源,同时操作接口封装、中断管理以及内存锁定与映射这三个部分来实现实时任务的运行:
[0042]操作接口封装:Windows系统实时性扩展,内核态下的两级调度,必然涉及到实时任务进程和线程的创建、终止、挂起和恢复操作。Windows系统本身并没有对外提供内核态下这些操作接口,但它们确实存在于内核中,因此有必要利用某些方法来获得进程线程操作的函数地址,供实时调度器使用。
[0043]借助于微软公司提供的系统服务描述符表SSDT,利用已知的Windows接口特征码,搜索系统内存指令,获取系统操作函数入口地址。获取该入口地址后,根据系统函数原型,利用嵌入汇编进行调用,完成特定操作接口的封装。
[0044]系统服务描述符表SSDT,把ring3级别的Win32API和ringO级别的内核API联系起来,是一个地址索引表。每一个用户API都对应一个固定的服务号,这个服务号就是该用户API所对应的内核API相对于地址索引基地址的偏移量。其作用相当于系统内部API的指向标,指明需要调用的API的内核地址。
[0045]内核操作接口寻址过程如图4所示,利用操作接口所对应的服务号和SSDT表的基地址,可以获得操作接口所对应的NT函数的内核地址,Windows系统中,Nt接口函数是原生函数,有大量的异常判断和错误处理操作,执行效率不高,对于实时任务的管理操作,可以选择更为底层的Ke接口函数。在Nt接口函数入口处,利用已知的Ke接口特征码,搜索系统内存指令,获取系统Ke操作函数入口地址。找到Ke操作函数入口地址后,根据系统函数原型,利用嵌入汇编进行调用,完成内核操作接口的封装。
[0046]在Windows xp SP3系统环境下,线程的创建、挂起、恢复和终止的服务号分别为0x35、0xFE、0xCE和0x102,挂起函数的特征码依次为0x89、0x45和OxFC,恢复函数的特征码依次为 0xFF、0x75 和 0x08。
[0047]中断管理:屏蔽实时核上除了时钟中断的其他中断,并为实时调度提供高精度调度信号。多核PC机的中断系统绝大多数都使用高级可编程中断控制器APIC来实现,每个CPU核心都有自己的Local APIC,并受主板上IOAPIC进行统一管理。外部设备连接到IOAPIC,IOAPIC再把外部中断分发给各个CPU的Local APIC处理。利用IOAPIC控制器编程使得中断重新定向,把外部中断定向给非实时内核进行处理,避免了外部中断对实时内核的影响。利用Local APIC控制器编程可设定时钟中断周期,使之能够产生10-100微秒的定时信号,为实时调度程序提供调度信号。
[0048]①使用IOAPIC控制器屏蔽外部中断
[0049]当一个IRQ发生时,IOAPIC控制器负责决定将IRQ发送给哪个CPU核心,以及以何种形式发送等,IOAPIC可以描述IRQ与中断向量号的映射,Windows会根据中断向量号来转移到相应的中断处理程序。
[0050]由Intel手册得知,IOAPIC单元包含一组总线输入信号,24个64位的中断重定向表、可编程的控制器和一个发送、接收APIC总线上APIC信息的寄存器。1/0设备通过在IOAPIC中申请一个中断信号线来向操作系统发送中断。然后IOAPIC在重定向表中选择相应的项并且使用项中的信息形成一条中断请求信息通过APIC总线发送给相应的CPU处理。重定向表中的每一项可以单独地被编程,可以指定它为edge/level敏感的中断信号、中断向量和优先级、目标处理器,以及处理器被选择的方式(静态或者是动态)。
[0051]本发明中只用到了 IOAPIC中的24个重定向表,APIC就是通过这24个重定向表实现了中断的“路由”功能。通过对重定向表重新编程,使外部中断只发给非实时CPU核处理,不发给实时CPU核,从而减轻实时CPU的负载,减少实时任务的延迟。
[0052]IOAPIC中的24个64位的重定向表,每一项都可连接外部中断,其中第56_63位表示把此中断发往哪些CPU处理。这八位二进制位从低到高分别表示8个CPU核,把相应比特位置为I,表示此中断需要发给此CPU核处理;置为O表示此中断不发给此CPU处理。在Windows中,IOAPIC的地址为0xFEC00000,通过读写其一系列的寄存器来实现编程控制,要访问10APIC,不能直接使用它的内部寄存器,而是要通过2个内存映射寄存器来访问,一个是10REGSEL,一个是10WIN,他们地址分别是0xFEC00000和OxFECOOO 10。利用2个映射的寄存器遍历24项重定向表,把每一个向量的第56-63位修改为相应的值即可。其原理如图5所示。
[0053]在本发明中,把重定向表项中的第57位置为1,把第56位以及第58-63位置为0,即可实现外部中断全部发给非实时核CPU1,而CPUO不会接收到外部中断。通过此设置即可完成中断资源的重分配,此时实时CPUO不再接收外部中断,所有的外部的中断都由非实时CPUl核处理,从而可以消除实时任务因为中断处理而造成的延迟。
[0054]②使用Local APIC产生高精度调度信号
[0055]多核PC中的Local APIC定时器为闲置状态,可以利用它来实现高精度的时钟信号,为调度提供触发脉冲。在Local APIC中,有初始计数寄存器和向量寄存器,初始计数寄存器填充的是计时间隔数(计时周期除以频率),向量寄存器填充的是达到计时间隔数后,进行处理的中断服务员程序入口地址。
[0056]在本发明中,根据调度周期,设置初始计数寄存器,指向独立的中断向量处理地址,并编写相应的中断服务程序,在中断服务程序里实现调度策略。从而实现了在PC中两个相互独立的时钟中断:一个是原有的Windows系统时钟中断,一个是新添加的实时时钟中断,两个时钟中断互不影响。
[0057]Local APIC控制器的基准频率是system bus频率,也就是外频,通过wmic指令可以直接获取外频数值。但是在实际应用中,会出现超频等现象,而该指令获取的值只是额定的出厂设置,还需要进一步完善。依据外频与主频有固定的倍数关系:主频=外频X倍频。获取CPU出厂的额定主频后,同上述获得的原始外频一起先计算出倍频。确定倍频之后,再次查询注册表或利用rdtsc汇编指令计算得出当前真实主频,从而反算得出真实的外频,此时得到的才是系统有效的总线频率。初始计数寄存器所填充的数值即为:调度周期/总线频率。具体操作步骤如图6所示。
[0058]获得计时间隔数后,在Local APIC控制器0xFEE00380处对应的初始计数寄存器中填写该数值,即完成调度定时周期的设置。在0xFEE00320处对应的设置寄存器中,设置其中断向量,使之指向实时调度入口。这样,每达到调度周期后,Local APIC控制器就会产生时钟中断,触发实时调度。
[0059]内存锁定与映射=Windows内存管理采用虚拟内存技术,它将暂时不使用的页面从内存中交换出去,而将需要使用的页面换入到内存中。对于实时任务而言,缺页换页所带来的时间开销是不能容忍的。同时,Windows任务往往运行在用户态,而用户态程序和内核态程序默认是不能直接通信的。当有数据需要传输和处理时,系统需要不断地在用户态和内核态之间切换,不断复制数据,这样就增加了任务完成时间,有可能造成超时。
[0060]Windows内核和应用程序使用虚拟地址来访问内存,一段连续的虚拟地址空间可能对应着许多物理页面,这些页面有可能是不连续的。Windows利用内存描述符链表MDL(Memory Descriptor List,内存描述表)来描述物理内存和虚拟内存之间的映射关系。
[0061]MDL的数据结构如图7所示:一段连续的虚拟地址空间对应着3个物理页面,这3个页面并不连续。MDL可以连接成一个链表,Next指向下一个MDL,Size表示存储MDL所用的内存大小,MdlFlags表示MDL的标记,当从内核态地址映射到用户态地址时,映射后的地址是与进程上下文相关的用户态虚拟地址,Process指向该进程,当从用户态映射到内核态时,MappedSystemVa表示映射后的内核态虚拟地址,虚拟内存的大小为ByteCount,第一个页面的地址是StartVa,虚拟内存的首地址相对于第一个页地址的偏移量是ByteOffset。[0062]把内核态内存映射到用户态时,首先调用1AllocateMdl接口来分配一个MDL,对于在未分页池中分配的内存,用MmBuidlMdlForNonpagedPool接口来初始化页码数组,再利用MmProbeAndLockPages接口锁定MDL的内存,并且指明对它的权限,最后调用MmMapLockedPagesSpecifyCache接口将内核态缓冲区映射到用户态地址空间,返回用户态地址空间的起始地址。
[0063]在本发明中,如图8所示,将调度所需数据结构,主要是将包含线程控制块TCB和进程控制块PCB的调度队列映射为用户可用地址,用户态实时任务中的调度队列和内核态实时调度器中的调度队列指向相同的物理内存,操作用户态实时任务中的调度队列等同于操作内核态实时调度器中的调度队列。
[0064]这样使调度算法的编写与调试可以在用户态进行,不需要模式切换,在用户态就可以修改内核态中调度队列的信息,方便调度程序的开发与替换。等调度策略确定后,把调度机制挂入Local APIC高精度时钟的中断处理例程中,从而周期性进行任务的切换与调 度。
[0065]5)任务切换。在两级调度中,实时任务将会被实时内核模块处理,利用Local APIC高精度时钟中断的DPC例程来执行调度策略。本发明中目前只提供了最常用的基于优先级的调度策略。优先级设定不限(大于0,数字越小优先级越高),优先级高者首先运行,可剥夺可抢占。
[0066]本发明中实时调度策略:有任务到达,首先按照线程固有优先级将其TCB插入READY_LIST。程序进行时,根据线程运行结果将状态改变的线程TCB直接变换链表。如SUSPEND_LIST和WAITING_LIST中恢复的线程(即状态为READY)插入READY_LIST中,或者本身需要挂起或等待的直接更改状态后插入阻塞或挂起队列。利用软中断陷入内核进行任务切换,在调度时首先需要将DELAY_LIST中开始时间已到的线程插入READY_LIST中。READY_LIST中是以优先级大小升序插入。也就是第一个节点是优先级最高的任务。至此,可以对当前调度情形做判断,查看最高优先级任务是否改变,是否挂起等。若没有改变则直接结束时钟中断处理,接着运行。如果有改变则进入DPC例程进行相关调度,选出READY_LIST中优先级最高且拥有运行资格(状态为READY)的线程,恢复其运行。编写的用户程序还可以在线程运行过程中动态的提高响应线程优先级,或直接改动线程所在链表。流程图如图9表示。
[0067]应用程序的编写主要是调用DLL提供的用户接口进行编写。以下是一个应用程序运行结果如图10所示。此程序一共运行3个线程,严格按照优先级运行,且支持抢占和线程同步。可以看出两级调度对于线程的调度控制可以满足实时要求。同时DLL提供的用户接口可以使用户方便的更改线程优先级等相关属性,加上模拟Windows本身我们提供了一套完整的同步机制。这为用户定制特定调度或执行顺序提供了良好的扩展基础。示例中3个线程信息如表1所示。执行结果如图10所示。可以看出用户通过优先级设定等操作可以实现线程的完全控制。系统满足实时性要求,可以有效的控制任务运行。
[0068]表1线程属性设定表
[0069]
线程序号 I优先级I任务切换
【权利要求】
1.一种Windows系统实时扩展的两级调度方法,其特征在于步骤如下: 步骤1:利用Windows系统中进程和线程CPU亲缘性标志位,对系统资源进行重新分配,把系统中所有进程和线程都指向非实时内核,空出运行实时任务的CPU核心,为实时任务提供足够的运行资源; 步骤2:同时操作接口封装、中断管理以及内存锁定与映射这三个部分来实现实时任务的运行: a)借助于微软公司提供的系统服务描述符表SSDT,利用已知的Windows接口特征码,搜索系统内存指令,获取进程和线程的创建、挂起、恢复、终止四个基本操作的函数入口地址,并根据系统函数原型,利用嵌入汇编进行调用,完成特定操作接口的封装; b)使用IOAPIC可编程中断控制器编程把外部中断分派给各个非实时内核,避免外部中断对实时内核的干扰,通过设定实时内核内的初始计数寄存器上的计数值,产生10-100微秒的定时信号,为实时调度程序提供调度信号; c)在内核态下预先申请连续的物理页面,对其进行锁定,防止该页面的换入换出,利用MDL机制,对该物理页面进行用户态映射,使用户态和内核态能够操作同一块物理空间,避免运行模式切换时造成的数据拷贝,将线程控制块TCB和进程控制块PCB映射为用户可用地址,在用户态下进行调度算法的编写与调试; 步骤3:系统在执行具体任务时,根据不同的任务类型,采用不同的处理内核调度策略,普通任务、Windows系统任务和外部中断使用非实时内核处理,采用Window系统自身的调度策略;实时任务、指定的特殊中断将会被实时内核模块处理,采用实时调度机制,从而实现Windows实时扩展的两级调度。
2.根据权利要求1所述一种Windows系统实时扩展的两级调度方法,其特征在于所 述步骤2中设定初始计数寄存器上的计数值的步骤如下: 步骤a:获取CPU出厂的额定主频和原始外频,通过这两个值计算出倍频,三者之间的关系为:主频等于外频乘上倍频; 步骤b:查询注册表或利用rdtsc汇编指令计算得出CPU当前真实主频,根据步骤a计算的倍频,反算得出真实的外频,得到系统有效的总线频率; 步骤c:初始计数寄存器所填充的数值即为:调度周期/总线频率。
3.根据权利要求1所述一种Windows系统实时扩展的两级调度方法,其特征在于所 述步骤3中实时调度机制的步骤如下: 步骤a:按照线程固有优先级将线程TCB插入READY_LIST ; 步骤b:根据线程运行结果将状态改变的线程TCB直接变换链表; 步骤c:利用软中断陷入内核进行任务切换,在调度时首先需要将DELAY_LIST中开始时间已到的线程插入READY_LIST中; 步骤d:对当前调度情形做判断,查看最高优先级任务是否改变:若没有改变则直接结束时钟中断处理,任务接着运行;若有改变则进入DPC例程进行相关调度,选出READY_LIST中优先级最高且状态为READY的线程,恢复其运行。
【文档编号】G06F9/48GK103744726SQ201410001033
【公开日】2014年4月23日 申请日期:2014年1月2日 优先权日:2014年1月2日
【发明者】杜承烈, 蒋泽军, 陈进朝, 王丽芳, 杨皓, 秦楠, 王兆文, 黄云婷 申请人:西北工业大学