Windows上的时钟漂移

我开发了一个跟踪业务事件的Windows服务。 它使用Windows时钟来标记事件。 但是,底层时钟会漂移很大(例如,每分钟会丢失几秒钟),特别是在CPU工作的时候。 我们的服务器使用Windows时间服务与域控制器保持同步,域控制器在底层使用NTP,但同步频率由域策略控制,并且无论如何,即使每分钟同步也仍然会产生显着的漂移。 除了使用硬件时钟之外,有没有什么技术可以使时钟更稳定?

Solutions Collecting From Web of "Windows上的时钟漂移"

时钟节拍应该是可预测的,但是在大多数PC硬件上 – 因为它们不是为实时系统设计的 – 其他I / O设备中断优先于时钟节拍中断,有些驱动程序在中断服务程序中做了大量的处理比延迟程序调用 (DPC)慢,这意味着系统可能无法提供时钟滴答中断,直到(有时)在发出信号之后。

其他因素包括总线主控I / O控制器,这些控制器会从CPU中窃取大量内存总线周期,导致在很长的时间内内存总线带宽不足。

正如其他人所说,随着元件值随温度变化,时钟产生硬件也可能会改变其频率。

Windows确实允许调整每个中断上实时时钟添加的时钟周期数量:请参阅SetSystemTimeAdjustment。 但是,这只有在可预测的时钟偏差的情况下才有效。 如果时钟稍微关闭,则SNTP客户端(“Windows时间”服务)将调整该时滞,以使时钟稍微更快或更慢,以趋向正确的时间。

我不知道这是否适用,但…

Windows有一个问题,如果用timeBeginPeriod()更改定时器分辨率,时钟会漂移。

实际上,Java的Thread wait() (和os::sleep() )函数的Windows实现中存在一个导致这种行为的错误。 在等待之前,它始终将定时器分辨率设置为1 ms,以便准确(无论睡眠时间长短),并在完成后立即恢复,除非其他线程仍处于睡眠状态。 这个设置/重置会混淆Windows时钟,这个时钟窗口的时间量是相当稳定的。

Sun 自2006年以来其实已经知道这个 ,并没有修正它,AFAICT!

因为这个原因,我们实际上已经把钟快了一倍! 一个简单的Java程序,在循环中睡1毫秒显示这种行为。

解决办法是自己设定时间分辨率,尽可能长时间保持。 使用timeBeginPeriod()来控制它。 (我们设置为1毫秒,没有任何不利影响。)

对于那些用Java编码的人来说,解决这个问题的更简单的方法是创建一个只要应用程序存在就睡觉的线程。

请注意,这将在全球范围内解决此问题,无论哪个应用程序是实际的罪魁祸首。

除了更频繁地重新同步时钟之外,除了获得新的主板之外,我不认为你能做的事情不多,因为你的时钟信号似乎没有正确的频率。

您可以在计划的任务.bat文件中运行“w32tm / resync”。 这适用于Windows server 2003。

http://www.codinghorror.com/blog/2007/01/keeping-time-on-the-pc.html

PC时钟通常应该准确到每天几秒钟之内。 如果您遇到大量的时钟漂移 – 按每分钟数量级 – 首先要检查的是您的交流电源。 我亲自观察到,将UPS连接到另一个UPS(顺便说一句,这是一个不允许的),每天都会增加几分钟。 从链中移除不必要的UPS修复时间问题。 我不是硬件工程师,但我猜测主板上的实时时钟芯片使用了电源中的某个时序信号。

如前所述,Java程序可能会导致这个问题。

另一个不需要修改代码的解决方案是添加虚拟机参数-XX:+ForceTimeHighResolution (可在NTP支持页面找到 )。

9.2.3。 Windows和Sun的Java虚拟机

Sun的Java虚拟机需要使用> -XX:+ ForceTimeHighResolution参数来启动,以避免丢失中断。

有关更多信息,请参阅http://www.macromedia.com/support/coldfusion/ts/documents/createuuid_clock_speed.htm

从引用的链接(通过Wayback机器 – 原始链接消失):

ColdFusion MX:CreateUUID提高Windows系统时钟速度

在Macromedia ColdFusion MX和更高版本中加载多次调用createUUID函数可能导致Windows系统时钟加速。 这是Java虚拟机(JVM)中Thread.sleep调用时间小于10毫秒(ms)导致Windows系统时钟运行速度较快的问题。 此行为最初是作为Sun Java Bug 4500388(developer.java.sun.com/developer/bugParade/bugs/4500388.html)提交的,并且已经针对1.3.x和1.4.x JVM进行了确认。

在ColdFusion MX中,createUUID函数具有1毫秒的内部Thread.sleep调用。 当createUUID被大量使用时,Windows系统时钟将每分钟增加几秒。 加速度与createUUID调用的数量和ColdFusion MX服务器的负载成正比。 Macromedia已经在Windows XP,2000和2003系统的ColdFusion MX和更高版本中观察到这种行为。

增加重新同步的频率。 如果在自己的网络上同步自己的主服务器,没有理由不每分钟同步一次。

同步更频繁。 查看W32Time服务的注册表项 ,特别是“期间”。 “SpecialSkew”听起来像会帮助你。

时钟漂移可能是温度的后果; 也许你可以尝试让温度更稳定 – 也许使用更好的冷却? 尽管如此,你永远不会松动漂移。

使用外部时钟(GPS接收器等)以及将CPU时间与绝对时间相关联的统计方法是我们在此使用的用于在分布式系统中同步事件的方法。

既然这听起来像你有一个大生意:

拿一台旧的笔记本电脑或者其他不怎么好的东西,但似乎有一个或多或少的可靠的时钟,称之为计时器。 计时员唯一的工作就是每隔(例如)2分钟,向服务器发送一条消息,告诉时间。 服务器不再使用Windows时钟作为时间戳,而是从计时器的最后一个信号加上自信号起经过的时间。 用手表每周检查一次或两次计时器的时钟。 这应该足够了。

你在运行什么服务器? 在桌面系统中,遇到这种情况的时间是启用了扩展频谱(FSB),导致中断时间的一些问题,这使得时钟成为问题。 可能要查看这是否是这些服务器之一的BIOS中的一个选项,并在启用时将其关闭。

另一个选项是编辑时间轮询时间间隔,并使用以下注册表键使其缩短很多,很可能您必须添加它(注意,这是一个DWORD值,值以秒为单位,例如600 10分钟) :

 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient\SpecialPollInterval 

这是一个完整的检查: KB816042

我曾经写过一个Delphi类来处理时间重新同步。 它被粘贴在下面。 现在我看到拉里·西尔弗曼提到的“w32tm”命令,我怀疑我浪费了我的时间。

 unit TimeHandler; interface type TTimeHandler = class private FserverName : widestring; public constructor Create(servername : widestring); function RemoteSystemTime : TDateTime; procedure SetLocalSystemTime(settotime : TDateTime); end; implementation uses Windows, SysUtils, Messages; function NetRemoteTOD(serverName :PWideChar; var buffer :pointer) : integer; stdcall; external 'netapi32.dll'; function NetApiBufferFree(buffer : Pointer) : integer; stdcall; external 'netapi32.dll'; type //See MSDN documentation on the TIME_OF_DAY_INFO structure. PTime_Of_Day_Info = ^TTime_Of_Day_Info; TTime_Of_Day_Info = record ElapsedDate : integer; Milliseconds : integer; Hours : integer; Minutes : integer; Seconds : integer; HundredthsOfSeconds : integer; TimeZone : LongInt; TimeInterval : integer; Day : integer; Month : integer; Year : integer; DayOfWeek : integer; end; constructor TTimeHandler.Create(servername: widestring); begin inherited Create; FserverName := servername; end; function TTimeHandler.RemoteSystemTime: TDateTime; var Buffer : pointer; Rek : PTime_Of_Day_Info; DateOnly, TimeOnly : TDateTime; timezone : integer; begin //if the call is successful... if 0 = NetRemoteTOD(PWideChar(FserverName),Buffer) then begin //store the time of day info in our special buffer structure Rek := PTime_Of_Day_Info(Buffer); //windows time is in GMT, so we adjust for our current time zone if Rek.TimeZone <> -1 then timezone := Rek.TimeZone div 60 else timezone := 0; //decode the date from integers into TDateTimes //assume zero milliseconds try DateOnly := EncodeDate(Rek.Year,Rek.Month,Rek.Day); TimeOnly := EncodeTime(Rek.Hours,Rek.Minutes,Rek.Seconds,0); except on e : exception do raise Exception.Create( 'Date retrieved from server, but it was invalid!' + #13#10 + e.Message ); end; //translate the time into a TDateTime //apply any time zone adjustment and return the result Result := DateOnly + TimeOnly - (timezone / 24); end //if call was successful else begin raise Exception.Create('Time retrieval failed from "'+FserverName+'"'); end; //free the data structure we created NetApiBufferFree(Buffer); end; procedure TTimeHandler.SetLocalSystemTime(settotime: TDateTime); var SystemTime : TSystemTime; begin DateTimeToSystemTime(settotime,SystemTime); SetLocalTime(SystemTime); //tell windows that the time changed PostMessage(HWND_BROADCAST,WM_TIMECHANGE,0,0); end; end. 

我相信Windows时间服务只实现了SNTP,这是NTP的简化版本。 完整的NTP实施考虑到您的时钟的稳定性,以决定同步的频率。

您可以在这里获得Windows的完整NTP服务器 。