杨 皓,江 南,杜承烈
(1.西北工业大学计算机学院,西安710129;2.中国船舶重工集团公司750试验场,昆明650051)
基于APIC的高精度定时器设计
杨 皓1,江 南2,杜承烈1
(1.西北工业大学计算机学院,西安710129;2.中国船舶重工集团公司750试验场,昆明650051)
在操作系统开发过程中需要一定精度的计时器支持。在Windows实时扩展改造过程中,针对原有定时精度低和波动较大的问题,提出一种基于高级可编程中断控制器(APIC)的高精度定时器设计方案。利用CPU片上结构APIC的计数寄存器编程,构建高精度时钟。运用内核驱动建立内核调度管理,通过内存映射提高用户态到内核态的数据传输速度,保证实时性能。利用DLL提供一组和内核交互的定时器接口供用户使用。实验结果表明,该方案有效解决了定时精度和稳定性问题,构造的定时器性能稳定,具有良好的实用性。
高级可编程中断控制器;定时器;高精度;稳定性;内存映射;实时性
在80x86体系机构上,操作系统内核和时钟常用到定时器电路。常见的时钟有实时时钟(RTC)、时间戳计数器(TSC)、可编程定时器(PIT)、高精度事件定时器(HPET)、高级可编程中断控制器(Advanced Programmable Interrupt Controller,APIC)定时器以及电源管理定时器等[1]。而在当前的操作系统环境下,虽然硬件性能达标,但系统提供的时钟精度往往不高,有些甚至达不到毫秒级别。在一些系统底层工程中,如操作系统的实时化改造,有时需要更高级别精度的定时器,来实现更为精密的操控。而多核系统中Local APIC定时器一般处于闲置状态[2]。同时高级可编程中断控制器处理之后硬件支持精度至少可以满足微秒精度[3]。本文基于APIC提出了Windows系统下自定义高精度实时器的实现方法。
在x86体系中,每个逻辑处理器都有自己的local APIC,这属于片上结构。从Pentium4和Intel Xeon处理器开始,APIC结构进一步扩展,形成了xAPIC体系,之后又在xAPIC基础上再进一步扩展形成了x2APIC体系[4]。结构的升级带来的更多的功能,但是基本功能还是保持不变的,其基本结构如图1所示。
图1 Local APIC结构
每个local APIC都包含了一组寄存器,用来控制本地和外部中断的产生、发送和接收,这些寄存器组均以内存映射的形式映射到物理地址空间,因此可以直接访问内存进行控制。APIC模式的寄存器则映射到MSR寄存器组,从而代替了内存映射。在进行操作时一般使用RDMSR和WRMSR指令完成[4]。
2.1 APIC计数相关寄存器
本文以local APIC寄存器映射到物理地址中为例。其寄存器的地址以APIC_BASE作为基地址,每个APIC寄存器都有自己对应的偏移量,如 local APIC ID寄存器偏移量为20H,那么它的地址就为APIC_BASE+20H。在系统中APIC_BASE的值一般为FEE00000H。
Local APIC定时器实质是由LVT timer寄存器编程产生APIC timer中断来实现的。这些寄存器主要有以下4种:
(1)LVT timer寄存器。
此寄存器偏移量是320H。它是LVT寄存器之一,使用定时器前,首先需要设置此寄存器。主要设置定时器的触发模式。Vector为中断描述符,寄存器描述如图2所示。
图2 LVT timer寄存器
(2)Divide configuration寄存器
此寄存器也叫分频寄存器,偏移量为3E0H。local APIC计数器使用固定的时钟频率,但通过此寄存器可以进行分频操作,从而确定计时精度。此基准时钟一般都是系统总线频率。以Intel处理器来说,也就是 CPU外频。该寄存器定义如图3所示。
图3 Divide Configuration寄存器
(3)Initial-count寄存器和Current-count寄存器
初始计数寄存器地址偏移量为380H,当前计数寄存器偏移量为390H。这2个寄存器成对使用。它们均是32位寄存器,计时开始前要设定初始计数寄存器的值。一旦此值设定,APIC timer将会复制此值到只读的当前计数寄存器(对用户而言)。计时开始, Current-count值会递减,直到为0产生时钟中断。
2.2 APIC timer计数模式
APIC timer模式设定由上文可知,主要是对于LVT timer寄存器的设定。它有3种计数模式,分别是One shot(一次性计数)、Period(定期计数)、TscDeadline(达到tsc值计数)。
当为One shot模式时,计数器在Current-count值为0产生一次时钟中断后,即会停止工作。除非重新写入一个Initial-count值,才会再次激活定时。若使用Period模式,当Current-count值为0时,产生时钟中断。而APIC会自动再次装载Initial-count的值到Current-count中,重新开始计数。这就会周期性的产生timer中断,除非Initial-count设定为0才会停止。TSC-deadline模式需要CPU另行支持,只用IA32_TSC_DEADLINE寄存器进行计数。此寄存器为64位,当TSC值大于或等于此寄存器的值时,将产生时钟中断[5]。
本文在Windows系统中基于Local APIC的计数器功能,设计实现了一种高精度定时器。此定时器在用户态提供了Sleep以及触发模式定时器的用户接口。定时器精度可以达到微妙级别,且具有良好的稳定性。
整个定时器系统设计为4个层次,最底层为APIC硬件支持层,其上是内核驱动层,内核驱动层中包含了APIC的关键操作,同时定时器的触发和管理主要在此层内完成。内核驱动之上是DLL交互层,提供一些用户接口与内核交互功能。如设定精度,注册定时器等,最上层为用户应用层。定时器系统框架如图4所示。
图4 定时器系统框架
3.1 内核驱动层设计实现
内核驱动是一个NT框架的驱动程序,主要是提供了一种内核访问操作的权限,使得可以映射APIC寄存器进行操作。根据APIC工作方式,设计了内核工作流程如图5所示。
图5 内核驱动层工作设计流程
初始化时主要是进行定时器相关数据结构初始化。由于定时器只是一种周期性任务,因此将定时器设定为一个线程内核对象,此对象应主要记录任务线程id、句柄以及唤醒时间等。系统管理数据主要包括了一组预先申请的线程内核对象以及等待和就绪链表的链表头。等待链表中的内核对象按照等待时间升序排列,每创建一个定时器,即生成一个内核对象插入此链表中。若定时器已经触发,任务开始运行的对象则插入就绪链表。
在驱动启动时,首先初始化上述管理数据。然后是添加APIC中断处理例程。目前的操作系统默认APIC都是开启的,Initial count默认为0,此时计数器并不工作产生中断。添加中断处理需要获取中断描述符表(IDT),在其中添加APIC timer中断处理程序描述符使其优先级最高,这样可以让其中断优先得到相应[6]。中断响应延迟影响着系统其它方面的实时性能[7]。在中断处理中基本不做有效工作,而是将定时器管理触发的大部分工作放入DPC例程中进行。这样可以有效地保证时钟计时的准确性。避免了在中断状态的时间过长问题。然后等待用户设定定时器精度。一旦用户设定精度通过Ioctl传入内核,则根据传入精度值设定寄存器。
中断处理添加完成之后,就需要设定定时器相关寄存器,使得APIC计数器开始工作。设定timer相关寄存器。主要是对LVT timer寄存器、分频寄存器和Initial count寄存器进行设置。设定timer为周期循环(Period)模式,分频为1。将Initial-count寄存器设定为传入数据后,计数开始。寄存器设定代码示例如下:
计数开始后,中断处理DPC例程中完成定时器的管理工作。每次中断处理中首先判断下次调度时间(当前超时最短任务时间)是否到达,若到达则插入DPC例程,在此例程中遍历等待链表,恢复唤醒时间已到的线程对象。由于此链表中的线程对象是按照唤醒时间升序排列的,因此遍历直到第一个唤醒时间未到的对象为止。根据等待链表第一个对象的唤醒时间更新下次调度时间,若空则说明没有任务,将其设为最大值[8]。
3.2 DLL交互层的设计与实现
在完成内核驱动之后,需要提供一个和内核交互的DLL交互层,以方便用户使用定时器。在此DLL种主要是实现了定时器基准频率获取、精度设定等功能,同时向用户提供了定时器创建、设置、退出等常用操作。基准频率获取是在用户加载DLL时自动完成的,而精度由用户设定。精度设定接口的实现可以采用Ioctl进行信息交换,利用Windows这种消息传递机制触发时钟计数[9]。此过程中由用户态将Initial count寄存器的值传入,进而设定。定时器相关接口则主要是将创建的任务信息注册到定时器内核中,同时进行一些设定或状态更改,可根据具体的数据结构进行设计实现。
在设定精度之前,首先要做的是获取定时器的基准频率。APIC定时器的基准频率是system bus频率,也就是外频,通过wmic指令可以直接获取外频数值。但是在实际应用中,由于可能会出现超频等现象,而此指令获取的值只是额定的出厂设置,因此还需要进一步完善。依据外频与主频有固定的倍数关系:主频=外频×倍频。获取CPU出厂的额定主频后,同上述获得的原始外频一起先计算出倍频。确定倍频之后,再次查询注册表或利用rdtsc汇编指令计算得出当前真实主频,从而反算得出真实的外频,此时得到的才是系统有效的总线频率。利用总线频率就可以设定Initial-count寄存器的值,也就是本文定时器精度。具体流程如图6所示。
图6 定时器精度设定流程
在设置定时器精度完成之后,需要完成定时器周期任务的创建与注册。所有的周期任务均可设定为一个标准的线程入口函数,在此函数中主要是一个while(1)的循环语句。在初始化定时器时,将用户定义的定时器任务入口地址及参数作为此标准线程的参数传入,在while语句中执行此函数,这样就可以达到重复执行的目的。每执行一次,更新此内核线程对象的唤醒时间,然后挂起线程。这样在时钟中断到来时,若下一次唤醒时间已到,在有DPC例程唤醒。定时器任务伪代码lpParaml如下:
其中,lpParam1为标准线程传入参数。此DLL交互层主要提供接口包括睡眠(Sleep)、创建定时器(CreateTimer)、设定定时器属性(SetTimerRelative)、退出定时器(CancelTimer)、删除定时器(DeleteTimer)。睡眠不需要创建定时器,系统会自动记录当前线程的id等信息,并维护一个唤醒时间和周期,同时将其加入定时器管理链表中。创建定时器,实际上就是利用上述任务逻辑,创建一个循环的标准线程,同时记录下相关信息。设定接口则主要是维护定时器结构中的唤醒时间和周期记录数据[10]。
4.1 精度测试
定时器精度由当前系统总线频率确定,在分频为1系统总线频率为200 MHz的情况下,理论上精度最高分辨率为5 ns[11]。实际上达不到这样的精度。因为定时器的处理以及系统上下文切换等会花费一些时间。在实际应用中建议精度设定到微妙或毫秒级别。由于直接采用时间戳计数器,并不稳定,因此测定采用HPET高精度计时器进行测定[12]。
一般Windows下精度只能到达毫秒精度,且稳定性比较差。对Windows和本文的定时器进行精度测定,在Sleep函数执行时睡眠时间逐次从1 ms开始增加到500 ms停止。记录此过程中每次睡眠的真实时间。图7是测试结果折线。为了更清楚的看清折线,此处选取了前200次记录作图。
图7 定时器精度测试结果
从图7可以看出,Windows定时功能准确度较差。15 ms以下精度均不能达到,而且精度增加过程呈阶梯状。相比APIC定时器精度可以有效达到1 ms,精度增加则基本呈线性增长。在使用过程中可以自由的设定时长,解决了系统定时精度差的不足。
4.2 稳定性测试
在对精度进行测试之后,笔者选取了10 ms, 50 ms,100 ms周期,分别在Windows系统和APIC定时器上进行了1 000次连续测试。此测试主要是为了探查2种定时器在运行过程中定时稳定性以及准确性的。
笔者对1 000次测试结果进行了统计,计算了测定周期的平均值和方差。统计结果如表1所示。可以看出,在准确度的表现上本文的定时器远远优于Windows本身定时器。同时方差相对于平均值波动,APIC定时器更为稳定。
表1 定时器稳定性测试统计结果
除统计结果外,笔者还选取了前500次测定结果作折线图如图8所示。从图8可以看出,本文设计的定时器准确性高于Windows系统本身。本文设计的定时器在运行时,测定时间与设定时间基本一致,而且波动不大。而Windows定时则不能准确地到达设定周期,准确率不够。
图8 定时器稳定性测定结果
本文在 APIC timer的基础上,设计了一种Windows系统下的自定义的高精度APIC定时器。以当前的系统总线频率200 MHz计算,该定时器精度最高可达到5 ns。在实际中因为时钟中断处理等本身需要占有一定时间,所以会低于此理论精度,在应用时精度设定到100 μs或ms以上。由于APIC硬件的支撑,该定时器准确率较高,稳定性好。因此,自定义精度可以很好地满足不同时钟精度需求,在对系统进行深度开发、实时改造等工作时可以提供有效的借鉴。
[1] Bovet D P,Cesati M.深入理解 LINUX内核[M].陈莉君,张琼声,张宏伟,译.北京:中国电力出版社,2009.
[2] 杜旭东,蒋泽军,王丽芳,等.基于资源重分配的Windows实时性改造[J].微电子学与计算机,2012, 29(5):193-195.
[3] 蒋善峰,王丽芳,蒋泽军.Windows时钟机制的实时扩展研究[J].微电子学与计算机,2012,29(8): 116-119.
[4] 邓 志.X86/x64体系探索及编程[M].北京:电子工业出版社,2012.
[5] Intel Corporation.Inte@64 and IA-32 Architectures Software Developer's Manual Volume 3B:System Programming Guide,Part 2[Z].2006.
[6] 潘 汉,莫苏苏.基于Local APIC的Windows 2000实时化改造[J].电子元器件应用,2009,11(10):80-83.
[7] 吴 讯,马 媛,董勤鹏.实时操作系统实时性能测试技术研究[J].系统仿真学报,2013,25(2):313-316.
[8] Gao Zhigang,Zhang Peifeng,Dai Guojun,et al.A New Implementation Method of Timer for Periodic Tasks[J]. Journal of Embedded Computing,2010,4(1):55-57.
[9] 林聚伟.Windows内核安全编程从入门到实践[M].北京:电子工业出版社,2012.
[10] 张志明,孙广清,王 磊.Windows下高精度软件定时器的研究与实现[J].微型机与应用,2003,22(1): 55-57.
[11] 候 峰,童晓阳.基于APIC时钟的嵌入式Linux内核实时化研究[J].现代电子技术,2010,(14):193-195.
[12] 廖小勇.基于PC/Windows环境的实时系统研究与实现技术[D].上海:同济大学,2002.
编辑 索书志
Design of High Precision Timer Based on Advanced Programmable Interrupt Controller
YANG Hao1,JIANG Nan2,DU Cheng-lie1
(1.School of Computer Science,Northwestern Polytechnical University,Xi'an 710129,China;
2.750 Testing Ground,China Shipbuilding Industry Corporation,Kunming 650051,China)
Timer with a certain precision is often required in the process of developing the system.In the Windows real time extending process,aiming at the problem of insufficient original timing accuracy and the fluctuation problem,this paper presents a high precision timer based on local Advanced Programmable Interrupt Controller(APIC).Making use of the counting register programs CPU sheet structure of APIC,it constructs high precision clock effectively,and uses the kernel driver construction scheduling management,memory mapping to improve data transmission speed of user state to guarantee real-time kernel to provide real-time.DLL provides a set of interface for users.Experimental results show that the scheme can effectively solve the problem of timing precision,and it has good usability.
Advanced Programmable Interrupt Controller(APIC);timer;high precision;stability;memory mapping; real-time
1000-3428(2014)09-0317-05
A
TP391
10.3969/j.issn.1000-3428.2014.09.063
国家部委基金资助项目。
杨 皓(1988-),男,硕士,主研方向:实时操作系统;江 南,研究员;杜承烈,教授、博士生导师。
2013-08-05
2013-10-10E-mail:Email:yanghao525@163.com