基于 X86 架构的时间管理虚拟化

作者:

Zachary Amsden <zamsden@redhat.com>

版权所有:
  1. 2010,Red Hat。保留所有权利。

1. 概述

X86 平台中最复杂的部分之一,特别是该平台的虚拟化,是可用的计时设备过多以及仿真这些设备的复杂性。此外,时间的虚拟化引入了一系列新的挑战,因为它引入了超出访客 CPU 控制的时间多路复用划分。

首先,我们将描述可用的各种计时硬件,然后提出一些出现的问题和可用的解决方案,并为某些 KVM 访客类别提供具体建议。

本文档的目的是收集与时间管理相关的数据和信息,这些信息可能在其他地方难以找到,特别是与 KVM 和基于硬件的虚拟化相关的信息。

2. 计时设备

首先我们讨论可用的基本硬件设备。TSC 和相关的 KVM 时钟非常特殊,值得全面阐述,将在下一节中描述。

2.1. i8254 - PIT

最早可用的计时器设备之一是可编程中断计时器 (PIT)。PIT 具有固定的 1.193182 MHz 基准时钟和三个通道,可以编程为提供周期性或单次中断。这三个通道可以配置为不同的模式,并具有单独的计数器。通道 1 和 2 在原始 IBM PC 中不可用于通用用途,并且历史上用于控制 RAM 刷新和 PC 扬声器。现在,PIT 通常集成作为仿真芯片组的一部分,不使用单独的物理 PIT。

PIT 使用 I/O 端口 0x40 - 0x43。对 16 位计数器的访问通过对 I/O 端口的单字节或多字节访问来完成。有 6 种模式可用,但并非所有模式都适用于所有计时器,因为只有计时器 2 具有连接的门输入,这是模式 1 和 5 所必需的。门线由端口 61h 的位 0 控制,如下图所示

--------------             ----------------
|            |           |                |
|  1.1932 MHz|---------->| CLOCK      OUT | ---------> IRQ 0
|    Clock   |   |       |                |
--------------   |    +->| GATE  TIMER 0  |
                 |        ----------------
                 |
                 |        ----------------
                 |       |                |
                 |------>| CLOCK      OUT | ---------> 66.3 KHZ DRAM
                 |       |                |            (aka /dev/null)
                 |    +->| GATE  TIMER 1  |
                 |        ----------------
                 |
                 |        ----------------
                 |       |                |
                 |------>| CLOCK      OUT | ---------> Port 61h, bit 5
                         |                |      |
Port 61h, bit 0 -------->| GATE  TIMER 2  |       \_.----   ____
                          ----------------         _|    )--|LPF|---Speaker
                                                  / *----   \___/
Port 61h, bit 1 ---------------------------------/

现在描述计时器模式。

模式 0:单次超时。

这是一种单次软件超时,当门高电平时(对于计时器 0 和 1 始终为真)会向下计数。当计数达到零时,输出变为高电平。

模式 1:触发式单次。

输出最初设置为高电平。当门线设置为高电平时,启动倒计时(如果门降低则不会停止),在此期间,输出设置为低电平。当计数达到零时,输出变为高电平。

模式 2:速率发生器。

输出最初设置为高电平。当倒计时达到 1 时,输出变为低电平一个计数,然后返回高电平。该值被重新加载,倒计时自动恢复。如果门线变为低电平,则计数停止。如果输出在门降低时为低电平,则输出自动变为高电平(这仅影响计时器 2)。

模式 3:方波。

这会生成一个高/低方波。计数确定脉冲的长度,当达到零时,脉冲在高电平和低电平之间交替。只有当门为高电平时,计数才会进行,并且在达到零时会自动重新加载。计数在每个时钟递减两次,以便以完整的周期性速率生成完整的高/低周期。如果计数是偶数,则时钟保持高电平 N/2 个计数,低电平 N/2 个计数;如果时钟是奇数,则时钟保持高电平 (N+1)/2 个计数,低电平 (N-1)/2 个计数。只有偶数值才会被计数器锁存,因此读取时不会观察到奇数值。这是计时器 2 的预期模式,它通过对低通滤波方波输出生成类似正弦的音调。

模式 4:软件选通。

在编程此模式并加载计数器后,输出保持高电平,直到计数器达到零。然后,输出变为低电平 1 个时钟周期并返回高电平。计数器不会重新加载。仅当门为高电平时才进行计数。

模式 5:硬件选通。

在编程和加载计数器后,输出保持高电平。当门升高时,启动倒计时(如果门降低则不会停止)。当计数器达到零时,输出变为低电平 1 个时钟周期,然后返回高电平。计数器不会重新加载。

除了正常的二进制计数外,PIT 还支持 BCD 计数。命令端口 0x43 用于设置三个计时器中每个计时器的计数器和模式。

使用以下位编码向端口 0x43 发出的 PIT 命令

Bit 7-4: Command (See table below)
Bit 3-1: Mode (000 = Mode 0, 101 = Mode 5, 11X = undefined)
Bit 0  : Binary (0) / BCD (1)

命令表

0000 - Latch Timer 0 count for port 0x40
      sample and hold the count to be read in port 0x40;
      additional commands ignored until counter is read;
      mode bits ignored.

0001 - Set Timer 0 LSB mode for port 0x40
      set timer to read LSB only and force MSB to zero;
      mode bits set timer mode

0010 - Set Timer 0 MSB mode for port 0x40
      set timer to read MSB only and force LSB to zero;
      mode bits set timer mode

0011 - Set Timer 0 16-bit mode for port 0x40
      set timer to read / write LSB first, then MSB;
      mode bits set timer mode

0100 - Latch Timer 1 count for port 0x41 - as described above
0101 - Set Timer 1 LSB mode for port 0x41 - as described above
0110 - Set Timer 1 MSB mode for port 0x41 - as described above
0111 - Set Timer 1 16-bit mode for port 0x41 - as described above

1000 - Latch Timer 2 count for port 0x42 - as described above
1001 - Set Timer 2 LSB mode for port 0x42 - as described above
1010 - Set Timer 2 MSB mode for port 0x42 - as described above
1011 - Set Timer 2 16-bit mode for port 0x42 as described above

1101 - General counter latch
      Latch combination of counters into corresponding ports
      Bit 3 = Counter 2
      Bit 2 = Counter 1
      Bit 1 = Counter 0
      Bit 0 = Unused

1110 - Latch timer status
      Latch combination of counter mode into corresponding ports
      Bit 3 = Counter 2
      Bit 2 = Counter 1
      Bit 1 = Counter 0

      The output of ports 0x40-0x42 following this command will be:

      Bit 7 = Output pin
      Bit 6 = Count loaded (0 if timer has expired)
      Bit 5-4 = Read / Write mode
          01 = MSB only
          10 = LSB only
          11 = LSB / MSB (16-bit)
      Bit 3-1 = Mode
      Bit 0 = Binary (0) / BCD mode (1)

2.2. RTC

原始 PC 中可用的第二个设备是 MC146818 实时时钟。原始设备现在已经过时,通常由系统芯片组仿真,有时由 HPET 和一些 Frankenstein IRQ 路由仿真。

RTC 通过 CMOS 变量访问,该变量使用索引寄存器来控制读取哪个字节。由于只有一个索引寄存器,因此 CMOS 的读取和 RTC 的读取需要锁定保护(此外,允许用户空间实用程序(如 hwclock)直接访问 RTC 是危险的,因为它们可能会破坏内核对 CMOS 内存的读取和写入)。

RTC 生成一个中断,该中断通常路由到 IRQ 8。该中断可用作周期性计时器、额外的每天一次的警报,并且可以在 MC146818 完成 CMOS 寄存器的更新后发出中断。中断的类型在 RTC 状态寄存器中发出信号。

即使系统关闭,RTC 也会通过电池电源更新当前时间字段。在更新正在进行时,不应读取当前时间字段,如状态寄存器所示。

时钟使用 32.768kHz 晶体,因此如果要 RTC 计数秒,则寄存器 A 的位 6-4 应编程为 32kHz 分频器。

这是最初用于 RTC/CMOS 的 RAM 映射

Location    Size    Description
------------------------------------------
00h         byte    Current second (BCD)
01h         byte    Seconds alarm (BCD)
02h         byte    Current minute (BCD)
03h         byte    Minutes alarm (BCD)
04h         byte    Current hour (BCD)
05h         byte    Hours alarm (BCD)
06h         byte    Current day of week (BCD)
07h         byte    Current day of month (BCD)
08h         byte    Current month (BCD)
09h         byte    Current year (BCD)
0Ah         byte    Register A
                     bit 7   = Update in progress
                     bit 6-4 = Divider for clock
                                000 = 4.194 MHz
                                001 = 1.049 MHz
                                010 = 32 kHz
                                10X = test modes
                                110 = reset / disable
                                111 = reset / disable
                     bit 3-0 = Rate selection for periodic interrupt
                                000 = periodic timer disabled
                                001 = 3.90625 uS
                                010 = 7.8125 uS
                                011 = .122070 mS
                                100 = .244141 mS
                                   ...
                               1101 = 125 mS
                               1110 = 250 mS
                               1111 = 500 mS
0Bh         byte    Register B
                     bit 7   = Run (0) / Halt (1)
                     bit 6   = Periodic interrupt enable
                     bit 5   = Alarm interrupt enable
                     bit 4   = Update-ended interrupt enable
                     bit 3   = Square wave interrupt enable
                     bit 2   = BCD calendar (0) / Binary (1)
                     bit 1   = 12-hour mode (0) / 24-hour mode (1)
                     bit 0   = 0 (DST off) / 1 (DST enabled)
OCh         byte    Register C (read only)
                     bit 7   = interrupt request flag (IRQF)
                     bit 6   = periodic interrupt flag (PF)
                     bit 5   = alarm interrupt flag (AF)
                     bit 4   = update interrupt flag (UF)
                     bit 3-0 = reserved
ODh         byte    Register D (read only)
                     bit 7   = RTC has power
                     bit 6-0 = reserved
32h         byte    Current century BCD (*)
(*) location vendor specific and now determined from ACPI global tables

2.3. APIC

在奔腾及更高版本的处理器上,每个 CPU 都有一个板载计时器,作为高级可编程中断控制器的一部分。APIC 通过内存映射寄存器访问,并为每个 CPU 提供中断服务,用于 IPI 和本地计时器中断。

虽然理论上 APIC 是本地中断的安全稳定来源,但在实践中,由于 APIC CPU 本地内存映射硬件的特殊性,出现了许多错误和故障。请注意,CPU 勘误表可能会影响 APIC 的使用,并且可能需要解决方法。此外,其中一些解决方法对虚拟化提出了独特的约束 - 需要额外的开销(来自额外的内存映射 I/O 读取)或额外的功能,这些功能在实现上可能更耗费计算资源。

由于 Intel 和 AMD 手册对 APIC 进行了详细记录,因此我们在此避免重复细节。应该指出的是,APIC 计时器是通过 LVT(本地向量计时器)寄存器编程的,能够进行单次或周期性操作,并且基于总线时钟除以可编程分频器寄存器。

2.4. HPET

HPET 非常复杂,最初旨在取代 X86 PC 的 PIT/RTC 支持。这是否会成为现实还有待观察,因为 PC 硬件的事实标准是仿真这些旧设备。一些指定为无旧版支持的系统可能只支持 HPET 作为硬件计时器设备。

HPET 规范相当宽松和模糊,要求至少 3 个硬件计时器,但允许实现自由来支持更多。它也没有对计时器频率施加固定速率,但确实对频率、误差和偏移施加了一些极值。

一般来说,建议将 HPET 作为高精度(与 PIT/RTC 相比)的时间源,该时间源独立于本地变化(因为任何给定系统中只有一个 HPET)。HPET 也是内存映射的,其存在通过 BIOS 的 ACPI 表指示。

HPET 的详细规范超出了本文档的当前范围,因为它在其他地方也有非常详细的记录。

2.5. 板外计时器

一些专有卡(例如看门狗板卡)和常用卡(例如 e1000 网卡)内置了计时芯片,这些芯片可能有内核或用户驱动程序可访问的寄存器。据作者所知,尚未有人尝试使用这些芯片为 Linux 或其他内核生成时钟源,并且通常不鼓励这样做,因为它不符合约定的规则。这样的计时设备需要额外的支持才能进行正确的虚拟化,并且目前认为并不重要,因为没有已知的操作系统这样做。

3. TSC 硬件

TSC 或时间戳计数器理论上相对简单:它计数处理器发出的指令周期,可以用作时间的度量。但在实践中,由于许多问题,它是最复杂的计时设备。

TSC 在内部表示为一个 64 位 MSR,可以使用 RDMSR、RDTSC 或 RDTSCP(如果可用)指令读取。过去,由于硬件限制,可以写入 TSC,但通常在旧硬件上只能写入 64 位计数器的低 32 位,而计数器的高 32 位会被清零。然而,现在,在 Intel 0Fh 系列处理器(型号 3、4 和 6)和 06h 系列处理器(型号 e 和 f)上,此限制已解除,所有 64 位都可写入。在 AMD 系统上,写入 TSC MSR 的能力并非体系结构上的保证。

可以通过 CR4.TSD 位从 CPL-0 访问 TSC,并且有条件地为 CPL > 0 的软件访问。当启用该位时,会禁用 CPL > 0 的 TSC 访问。

一些供应商实现了一个额外的指令 RDTSCP,该指令不仅以原子方式返回 TSC,还返回与处理器编号相对应的指示符。这可以用于索引 TSC 变量数组,以确定 SMP 系统中 TSC 未同步时的偏移信息。必须通过查询 CPUID 功能位来确定此指令的存在。

VMX 和 SVM 都在虚拟化硬件中提供了扩展字段,允许客户机可见的 TSC 按常数偏移。较新的实现承诺允许额外缩放 TSC,但此硬件尚未广泛可用。

3.1. TSC 同步

在大多数实现中,TSC 是一个 CPU 本地时钟。这意味着,在 SMP 平台上,不同 CPU 的 TSC 可能会在不同的时间开始,具体取决于 CPU 的加电时间。通常,同一芯片上的 CPU 将共享同一个时钟,但情况并非总是如此。

BIOS 可能会尝试在加电过程中重新同步 TSC,并且操作系统或其他系统软件也可能会尝试这样做。几个硬件限制使问题变得更糟——如果无法写入 TSC 的全部 64 位,则可能无法将新到达的 CPU 中的 TSC 与系统其余部分的 TSC 匹配,从而导致 TSC 不同步。这可能由 BIOS 或系统软件完成,但在实践中,除非所有值都从同一时钟读取,否则不可能获得完全同步的 TSC,而这通常只在单插槽系统或具有特殊硬件支持的系统上才有可能。

3.2. TSC 和 CPU 热插拔

正如已经提到的,在系统启动时间之后到达的 CPU 可能没有与系统其余部分同步的 TSC 值。系统软件、BIOS 或 SMM 代码实际上可能会尝试将 TSC 设置为与系统其余部分匹配的值,但通常不能保证完全匹配。这可能会导致系统从 TSC 同步状态返回到 TSC 同步缺陷(无论多么小)可能暴露给操作系统和任何虚拟化环境的状态。

3.3. TSC 和多插槽/NUMA

多插槽系统,特别是大型多插槽系统,很可能具有单独的时钟源,而不是单一的、普遍分布的时钟。由于这些时钟由不同的晶体驱动,因此它们的频率不会完全匹配,并且温度和电气变化会导致 CPU 时钟(以及 TSC)随着时间的推移而漂移。根据精确的时钟和总线设计,漂移可能或不可能在绝对误差中固定,并且可能会随着时间的推移而累积。

此外,非常大的系统可能会故意偏移各个核心的时钟。这种称为扩频时钟的技术可以减少时钟频率及其谐波的 EMI,这可能是通过 FCC 电信和计算机设备标准所必需的。

由于这些原因,建议不要信任 TSC 在 NUMA 或多插槽系统上保持同步。

3.4. TSC 和 C 状态

C 状态,或处理器的空闲状态,尤其是 C1E 和更深的睡眠状态,也可能会对 TSC 产生问题。TSC 可能会在这样的状态下停止前进,导致在恢复执行时 TSC 落后于其他 CPU。此类 CPU 必须由操作系统根据 CPU 和芯片组标识进行检测和标记。

在这种情况下,可以通过将其追赶到已知的外部时钟源来更正 TSC。

3.5. TSC 频率变化/P 状态

为了使情况更有趣,一些 CPU 可能会改变频率。它们可能以相同的速率运行 TSC,也可能不以相同的速率运行 TSC,并且由于频率变化可能会交错或偏移,因此在某些时间点,除了落在一定的值范围内之外,TSC 速率可能未知。在这种情况下,TSC 将不是一个稳定的时间源,必须针对已知的、稳定的外部时钟进行校准,才能成为可用的时间源。

TSC 是否以恒定速率运行或是否随 P 状态缩放取决于型号,必须通过检查 CPUID、芯片组或特定于供应商的 MSR 字段来确定。

此外,一些供应商存在已知的错误,即在正常操作期间会正确补偿 P 状态,但当处理器处于非活动状态时,P 状态可能会暂时升高以服务来自其他处理器的缓存未命中。在这种情况下,已停止 CPU 上的 TSC 可能会比未停止的处理器上的 TSC 前进得更快。已知 AMD Turion 处理器存在此问题。

3.6. TSC 和 STPCLK/T 状态

提供给处理器的外部信号也可能具有停止 TSC 的效果。这通常是为了进行热紧急电源控制以防止过热情况而完成的,并且通常无法检测到这种情况的发生。

3.7. TSC 虚拟化 - VMX

VMX 提供了 RDTSC、RDMSR、WRMSR 和 RDTSCP 指令的有条件捕获,这足以以任何方式完全虚拟化 TSC。此外,VMX 允许传递主机 TSC 加上 VMCS 中指定的附加 TSC_OFFSET 字段。必须使用特殊指令来读取和写入 VMCS 字段。

3.8. TSC 虚拟化 - SVM

SVM 提供了 RDTSC、RDMSR、WRMSR 和 RDTSCP 指令的有条件捕获,这足以以任何方式完全虚拟化 TSC。此外,SVM 允许传递主机 TSC 加上 SVM 控制块中指定的附加偏移字段。

3.9. Linux 中的 TSC 功能位

总而言之,除非体系结构明确保证,否则无法保证 TSC 保持完全同步。即使如此,多插槽或 NUMA 系统中的 TSC 仍然可以独立运行,尽管它们在本地是一致的。

以下功能位由 Linux 用来指示各种 TSC 属性,但它们只能被认为对 UP 或单节点系统有意义。

X86_FEATURE_TSC

硬件中提供 TSC

X86_FEATURE_RDTSCP

提供 RDTSCP 指令

X86_FEATURE_CONSTANT_TSC

TSC 速率在 P 状态下不变

X86_FEATURE_NONSTOP_TSC

TSC 不会在 C 状态下停止

X86_FEATURE_TSC_RELIABLE

跳过 TSC 同步检查 (VMware)

4. 虚拟化问题

由于出现了一些挑战,因此计时对于虚拟化尤其成问题。最明显的问题是,时间现在在主机和(可能)许多虚拟机之间共享。因此,虚拟操作系统不会以 100% 的 CPU 使用率运行,尽管它很可能会做出这样的假设。当禁用中断源时,它可能希望它保持在非常精确的范围内,但实际上只有其虚拟中断源被禁用,并且机器仍可能随时被抢占。这会导致问题,因为真实时间的流逝、机器中断的注入和相关的时钟源不再与真实时间完全同步。

这种相同的问题在本地硬件上也会在一定程度上发生,因为当 BIOS 使用 SMM 模式时,SMM 模式可能会从自然而然的 X86 系统中窃取周期,但不会以如此极端的方式进行。但是,SMM 模式可能会导致类似于虚拟化的问题这一事实使其成为解决裸机上的许多这些问题的一个很好的理由。

4.1. 中断时钟

传统操作系统出现的最直接的问题之一是,系统计时例程通常被设计为通过计数周期性中断来跟踪时间。这些中断可能来自 PIT 或 RTC,但问题是相同的:主机虚拟化引擎可能无法每秒传递适当数量的中断,因此客户机时间可能会滞后。如果选择了较高的中断率(例如 1000 HZ),这将尤其成问题,不幸的是,这是许多 Linux 客户机的默认值。

解决这个问题有三种方法;首先,可能可以直接忽略它。对于那些有独立的时钟源来跟踪“挂钟时间”或“实时”的虚拟机,可能不需要调整其中断来保持正确的时间。如果这还不够,可能需要向虚拟机注入额外的中断,以提高有效中断率。这种方法在极端情况下会导致复杂情况,例如当宿主机负载或虚拟机延迟过大而无法补偿时。因此,出现了另一种解决方案:虚拟机可能需要意识到丢失的节拍并进行内部补偿。尽管理论上很有前景,但在 Linux 中实现此策略非常容易出错,并且许多常用的 Linux 系统中都分布着许多有缺陷的丢失节拍补偿变体。

Windows 使用周期性的 RTC 时钟作为内部保持时间的方式,因此需要中断偏移来保持正确的时间。然而,它使用的速率足够低(编者注:是 18.2 Hz 吗?),因此在实践中尚未成为问题。

4.2. TSC 采样和序列化

作为可用的最高精度时间源,CPU 的周期计数器引起了开发人员的极大兴趣。如上所述,该计时器具有许多独特的固有问题,例如它是一个本地的、可能不稳定且可能未同步的源。一个并非 TSC 独有的问题,但由于其非常精确的特性而突出显示的问题是采样延迟。根据定义,计数器一旦被读取,就已经过时了。然而,计数器也有可能在实际使用结果之前被读取。这是指令流超标量执行的结果,指令流可能乱序执行。这种执行称为非序列化执行。为了使用 TSC 进行精确测量,必须强制进行序列化执行,这需要一条序列化指令,例如 CPUID 或 MSR 读取。

由于 CPUID 实际上可以通过陷入和模拟机制进行虚拟化,因此对于硬件虚拟化,这种序列化可能会带来性能问题。因此,准确的时间戳计数器读数可能并不总是可用的,并且实现可能需要防范来自其他 CPU 的 TSC “向后”读取,即使在其他方面完全同步的系统中也是如此。

4.3. 时间规格别名

此外,当使用针对另一个时间源测量的 TSC 结果时,TSC 的这种非序列化会带来另一个挑战。由于 TSC 的精度高得多,因此当另一个时钟仍表示相同的值时,可能会读取 TSC 的许多可能的值。

也就是说,你可能会读取 (T,T+10),而外部时钟 C 保持相同的值。由于非序列化读取,你最终可能会得到一个波动的范围 - 从 (T-1..T+10)。因此,任何从 TSC 计算但根据外部值校准的时间都可能具有有效值的范围。重新校准此计算实际上可能会导致校准后计算的时间与校准前计算的时间相比向后倒退。

这个问题在 Linux 中的内部时间源(内核时间)中尤其突出,内核时间以理论上的高分辨率 timespec 表示,但以更大的粒度间隔前进,有时以 jiffies 的速率,并且可能在赶超模式下以更大的步幅前进。

这种别名需要谨慎计算和重新校准 kvmclock 以及任何其他从 TSC 计算得出的值(例如 TSC 虚拟化本身)。

4.4. 迁移

虚拟机的迁移在两个方面引发了时间保持的问题。首先,迁移本身可能需要时间,在此期间无法传递中断,并且之后,虚拟机时间可能需要赶上。NTP 可以在一定程度上帮助解决这个问题,因为所需的时钟校正通常足够小,可以落在 NTP 可校正的窗口内。

另一个需要关注的问题是,基于 TSC(或 HPET,如果暴露了原始总线时钟)的计时器现在可能以不同的速率运行,这需要在虚拟机管理程序中通过虚拟化这些计时器以某种方式进行补偿。此外,迁移到更快的机器可能会排除使用直通 TSC,因为更快的时钟无法在不潜在地使时间比平时更快地前进的情况下对虚拟机可见。较慢的时钟问题较小,因为它总是可以赶上原始速率。KVM 时钟通过简单地存储相对于 TSC 的乘数和偏移量供虚拟机转换回纳秒分辨率值来避免这些问题。

4.5. 调度

由于调度可能基于精确的计时和中断触发,操作系统的调度算法可能会受到虚拟化的不利影响。理论上,这种影响是随机的并且应该是普遍分布的,但在人为以及真实场景(虚拟机设备访问、虚拟化退出的原因、可能的上下文切换)中,情况可能并非总是如此。这种影响尚未得到充分研究。

为了解决这个问题,一些实现提供了一个半虚拟化的调度器时钟,该时钟揭示了虚拟机已运行的真实 CPU 时间量。

4.6. 看门狗

在硬件虚拟化下运行时,由于定时器中断延迟或对实际时间流逝的误解,看门狗定时器(例如 Linux 中的锁检测器)可能会意外触发。通常,这些警告是虚假的,可以忽略,但在某些情况下,可能需要禁用此类检测。

4.7. 延迟和精确计时

在虚拟化系统中,可能无法实现精确的计时和延迟。如果系统正在控制物理硬件,或者发出延迟以补偿设备之间速度较慢的 I/O 时,可能会发生这种情况。第一个问题对于虚拟化系统来说通常是无法解决的;硬件控制软件在没有完整的实时操作系统的情况下无法充分虚拟化,这将需要一个支持 RT 的虚拟化平台。

第二个问题可能会导致性能问题,但这不太可能是一个重大问题。在许多情况下,可以通过配置或半虚拟化来消除这些延迟。

4.8. 隐蔽通道和泄漏

除了上述问题之外,时间信息不可避免地会泄漏给虚拟机,了解宿主机,除非虚拟化时间的实现非常完美。这可能允许虚拟机推断出虚拟机管理程序的存在(如红丸类型检测),并且它可能允许通过使用 CPU 利用率本身作为信令通道在虚拟机之间泄漏信息。防止此类问题需要完全隔离的虚拟时间,这可能不再跟踪真实时间。这在某些安全或质量保证上下文中可能很有用,但通常不建议在实际部署场景中使用。