虚拟机中GUEST OS时钟(TIMEKEEP)问题的探讨

                                                                                                                                 康华

在第一篇文章中主要讨论了虚拟机中TIMEKEEP设计到的时钟中断注射问题,本文主要讨论虚拟机中各种平台时钟之间的同步问题。

 

传统操作系统中的时钟同步概念描述

我们知道系统硬件中存在诸如RTC/PIT/HPET/ACPI PM TIMER/TSC等许多时钟, 这些时钟按其特点来说可分成两类:以中断形式存在的周期性时钟, RTC/PIT/HPET等;另外一种是以COUNTER计数器存在的单步递增性时钟,如TSC。它们之间的区别在于,周期性时钟是通过周期性发送中断达到记时目的的,如心跳频率一般。而单步递增时钟则不发送中断,而时需要软件主动去读取其COUNTER寄存器来获得时间的。

因而操作系统会有选择的使用周期性时钟作为timekeep的时钟源,而使用单步递增时钟作时间校准或者性能统计之用。Linux系统window系统都是如此。

那么我们这里所指的时钟同步是说:系统中的周期性时钟需要和单步递增时钟同步,举例来说。假如操作系统使用RTC时钟作为TIMEKEEP的周期时钟源,且频率为100HZ,系统CPU频率 3000MH,TSC频率等同于CPU频率,那么应该每一次RTC中断产生的同时(系统时间前进了1/100 秒),TSCCOUNTER也应增加1/100*3000M。如果上述关系稳定,那么我们就称系统时钟同步。

那么为什么需要TSCRTC同步呢?操作系统的TIMEKEEP过程中往往(至少Linux系统会这样作,window可能没有)会使用TSC时间对RTC维持的时间(在Linux中就时jiffies)进行校正,这是因为RTC周期性中断有可能偶然丢失(比如,中断屏蔽等),而TSC计数器会稳定运行,不会丢失时间,因此RTC维持的系统计时就会落后于TSC计时,因此需要不断的对RTC计时进行校准,也就是说让其和TSC计时一致(具体校准动作在Linux系统中请看timer_interruptcur_timer所指的函数timer_tsc)。

在传统环境下的操作系统无论TSC或是RTC都是硬件负责运转,因此只要硬件稳定,那么TSCRTC等虽然是独立运行,但都应该是同步的。因此同步问题在传统环境下按理不成问题!

虚拟机中模拟时钟之间的同步

如果说在传统环境下时钟同步不成问题,那么在虚拟环境下时钟同步会有什么问题呢?

虚拟环境和传统环境下时钟产生的条件变化了!RTC等周期性时钟不是由相应硬件产生而是通过软件定时器模拟而得(具体可见上一篇文章),但是TSC的获得则不是通过模拟,还是读硬件的TSCCOUNTER 寄存器获得TSC值(只所以没有模拟是因为我们无法通过软件模拟出一个稳定单步增长的TSC COUNTER,直接读取硬件的TSC更简单且合理)。我们已经知道在虚拟环境下软件模拟RTC等周期性时钟中断无法精确的触发和递交给GOS(虚拟环境下肯定会延迟且触发间隔不稳定),RTC计时和TSC时间就发生了失步(TSC会跑的快)。在Linux系统上的现象就时输出打印出很多信息Losing too many ticks!(对应代码在timer_tsc中)。

对于Linux这样需要TSC校正周期性时钟计时的系统,我们需要想办法将TSCRTC等同步。方法其实intelamd的虚拟技术处理器已经给我们提供,就是在GOS读取TSC时(x86体系中有专门指令rdtsc),返回结果是在原结果(硬件TSC COUNTER)上再加上一个可变化,可配置的偏移值tsc_offset的和,因此我们可以通过设置这个tsc_offset值,达到将GOS看到的TSC和其RTC维护的计时同步。具体做法就是在每一周期时钟中断注射时刻,都根据GOS的计时(RTC维护)的时间设置新的tsc_offset(xenvpt.c中的pt_intr_post中的语句hvm_set_guest_time(pt->vcpu, pt->last_plt_gtime) 就是完成该工作的),这样以来确保了虚拟环境下时钟同步。

SMP环境下的同步

上面所讲的同步都是针对单核系统而言的,对于多核SMP环境的同步除了上述要求之外,还需要多个核和上的时间同步――各个核上的TSC需要同步(每个核都有各自的TSC),TSC和系统周期性时钟需要同步。

虚拟环境下多核同步有很多困难,想要完全同步要求多个模拟的核同步调度(同时被调度或被调出),否则有可能在给某个核提交周期时钟中断时,该核被调出,那么显然就会丢失时钟中断造成系统时间落后。就Xen代码而言,目前为了提高整个系统性能各个核独立调度,因此的确存上述丢失中断的可能。

为此xen 将周期性时钟绑定到BSP核上。由于AP收到时钟中断,以来Ap核不作timekeep操作(timekeep是在收到时钟中断后进行),所以不会进行系统计时操作和校对时间操作,也就不会报错Losing too many ticks!,虽然此刻AP核上的TSC确实和RTC计时失步。

不过虚拟双核无法保证两个核的TSC同步,这点会影响很多性能测试工具的正常运行,因为很多性能测试工具(pcmark,winsat等)都会利用TSC作性能测量的,所以你很可能会发现这些攻击在虚拟双核下打分很低或者无法使用。这个问题对于xen来说还没有解决或者说没有被重视。

如果想要彻底解决多核时钟问题,需要作很多辅助工作,比如在递交周期时钟中断时,触发ipi中断通知其它核进行相应同步处理;在某个核被调度出去后,其它核的TSC也应该冻结等。

 

小结:

虚拟环境下时钟问题有许多需要研究和完善的地方,我们两篇文章这种就时钟补偿和同步进行了初步讨论。虚拟时钟的实现在虚拟化技术中是一个很重要的技术点,它会对系统整体性能和稳定性有重大影响。目前的xen代码对时钟的虚拟还存在很多漏洞,希望有兴趣的读者可以继续研究。