介绍:
时间是操作系统(例如 Linux)的典型特征,它来自时钟芯片(RTC),或者由软件使用周期性中断或某些硬件寄存器(例如 CPU 的 TSC 循环计数器)来实现。
显然,虚拟机中没有直接的硬件访问(例如 RTC),因此保持正确的时间可能很棘手。
具体来说,我对两种 POSIX 时钟实现感到好奇:CLOCK_REALTIME
和CLOCK_MONOTONIC
(还有更多)。
骚乱
我正在考虑两个主要的“干扰”:
- “CPU 过度使用”:为虚拟机提供的虚拟 CPU 数量多于物理 CPU 数量
- “实时迁移”:将虚拟机从一台机器移动到另一台机器,而不会影响操作
普通手术
在裸机上运行的操作系统中的进程仅被操作系统(当时拥有控制权)中断。因此操作系统可以轻松保持时间。
虚拟机操作
在虚拟机中运行的操作系统无法持续控制 CPU。例如,如果操作系统“没有 CPU”,它就无法处理定时器中断。反过来,这可能会导致定时器中断完全丢失,延迟一些看似随机的量(抖动),甚至可能以快速顺序处理(现在处理“延迟”中断)。同样,时钟也不会像预期的那样线性前进。
选择
CLOCK_REALTIME
:如果操作系统缺少 CPU,实时时钟可能会变慢(落后),或者偶尔向前跳动以跟上CLOCK_MONOTONIC
:如果操作系统缺少 CPU,实时时钟可能会变慢(相对于其他虚拟机或挂钟时间),或者偶尔向前跳动以跟上
效果
CLOCK_REALTIME
:显然,如果实时时钟很慢,它就不能用作绝对计时测量,但它在 VM 中看起来是一致的。如果时钟通过向前跳跃可变的时间量来跟上,它可以用作绝对测量,但它不利于测量 VM 中的任何性能(持续时间)。CLOCK_MONOTONIC
:仅当虚拟机“拥有 CPU”时,单调时钟才会提供虚拟机内经过时间的一致视图。使时钟向前跳动可变的时间量将阻止在虚拟机内进行性能(持续时间)测量。
实时迁移
当实时迁移需要将数 GB 的 RAM 从一个节点复制到另一个节点时,虚拟机无法运行会有一些“冻结时间”,比如说 3 秒。
现在实时时间也应该向前跳 3 秒吗?还是应该将这 3 秒缩短,直到稍后手动或自动更正?同样,当使用单调时钟测量“正常运行时间”时,是否应该通过添加这 3 秒来考虑这 3 秒,还是应该考虑虚拟机实际拥有 CPU 的时间?
过度使用 CPU
与上述类似,但短暂延迟更频繁,而不是偶尔出现较大延迟。
问题
Xen 使用什么方法?
VMware 如何处理这个问题?有可配置的选项吗?(我知道在 Xen 中,虚拟机可以从虚拟机管理程序同步,也可以独立运行(例如,使用 NTP 从外部同步))
有什么“最佳实践”吗?
答案1
POSIX(以及一般的 Linux)从来没有真正保证过计时器,也就是说,如果你让某个东西进入睡眠状态,你就可以期望它在某个特定的时间被唤醒。你只能保证唤醒发生在该时间之后,而不是恰好在该时间。绝不在它之前*。
Linux 并不意味着是实时的,它只是尽力而为。
符合man 2 nanosleep
POSIX 标准的是:
nanosleep() 暂停调用线程的执行,直到 至少*req 中指定的时间已经过去,或者传递了一个信号,该信号触发了调用线程中的处理程序的调用或终止了该进程。
如果您期望滴答声可靠,那么问题很可能是您没有采用启发式方法来管理给定窗口内的幻灯片。
我的建议是重新考虑您的应用程序设计,使其在精确唤醒时不太可靠,或者在出现意外延迟的情况下设置故障保护装置。
IE
- 由于某些延迟异常,软件中止。
- 唤醒时的软件会注意到与其他权威时间源之间的差异,并“采取”下一次唤醒的想法进行补偿。
- 您可以打印警告或提供其他通知。
在可抢占系统中,认为时间是可靠的其实不太合理。即使在裸机上也是如此。
- 不可屏蔽中断无法被阻止。
- 高负荷意味着你已经安排了很长时间。
- 硬件调用的 CPU 中断可能会造成延迟。
- 小页面错误和大页面错误可能会导致计时器唤醒之间产生很长的延迟。
- CPU 在非自有内存组上分配内存会增加延迟。
这实际上只是现代 x86 计算的一个功能。
至少在 KVM 上,有一个名为“kvm-clock”的时钟源,它应该表示来自底层虚拟机管理程序的滴答声,而不管 VM 中是否存在任何未知延迟。您可以在此路径中找到该文件以及您已设置的内容:/sys/devices/system/clocksource/clocksource*/current_clocksource
并查看您的选项/sys/devices/system/clocksource/clocksource*/available_clocksource
。
但同样,底层主机可能会有自己的延迟。所以它只是一路向下的乌龟。
不要依赖不存在的实时保证。构建软件以应对意外延迟或至少了解它们。
NTP 总体而言是一整套协议,用于处理时间问题,什么时间是“正确的”,以及如何处理时间变化。这是一个相当复杂的问题。
最佳做法是,您希望设置系统以在统计上使问题不太可能发生,思考什么(如果有的话)将构成您应用程序中时间的可靠权威,然后考虑如何处理时间确实发生变化的不太可能发生的事件。
也许您设置了一些 SLA,说明在 1000000 个样本中,时间会有 1 次检查不正确。也就是说,尽管从统计上看,滴答声不太可能出现,但这种情况是有可能发生的。
在处理相互关联的不同系统组时,我考虑时间的方式是,它们的时间局部性* 差异在一个很小的窗口内更为重要。为此,我会设置一个本地时间服务器,它本身使用一些权威来源,然后让该组中的所有计算机同步到该本地系统。本地时间服务器的极低延迟有助于减少本地抖动,并且所有主机应保持非常紧密的同步。
一些计时器实现使用信号处理程序来捕获事件。例如,如果您在计时器之外向进程发送 ALRM 信号,则该进程会在此之前被唤醒。
此处的局部性是指所有计算机在逻辑上相互关联,彼此之间的时间差可能在几毫秒内。但它们在另一个局部性之间可能存在巨大差异,例如一组系统之间的延迟相差 500 毫秒。