我开发了一项跟踪商业活动的Windows服务。它使用Windows时钟为事件添加时间戳。但是,基础时钟可能会非常显着地漂移(例如,每分钟丢失几秒),尤其是当CPU正在努力工作时。我们的服务器使用Windows时间服务与域控制器保持同步,域控制器使用NTP,但同步频率由域策略控制,无论如何甚至每分钟同步仍然会带来显着的漂移。除了使用硬件时钟之外,还有什么技术可以让时钟更稳定吗?
答案 0 :(得分:17)
时钟滴答应该是可预测的,但在大多数PC硬件上 - 因为它们不是为实时系统设计的 - 其他I / O设备中断优先于时钟滴答中断,有些驱动程序在中断服务程序中进行大量处理,而不是将其推迟到deferred procedure call(DPC),这意味着系统可能无法提供时钟节拍中断,直到(有时)发出信号后很长时间。
其他因素包括总线控制I / O控制器,这些控制器从CPU中窃取了许多内存总线周期,导致它在很长一段时间内缺乏内存总线带宽。
正如其他人所说,随着元件值随温度的变化,时钟生成硬件也可能会改变其频率。
Windows允许调整每个中断上添加到实时时钟的滴答数量:请参阅SetSystemTimeAdjustment。但是,只有当您有可预测的时钟偏差时,这才有效。如果时钟稍微关闭,SNTP客户端(“Windows时间”服务)将调整此偏斜,使时钟略微更快或更慢,以趋向正确的时间。
答案 1 :(得分:12)
我不知道这是否适用,但是......
Windows存在一个问题,即如果用timeBeginPeriod()更改定时器分辨率,时钟就会漂移。
实际上,Java的Thread wait()
(以及os::sleep()
)函数的Windows实现中存在一个导致此行为的错误。它始终在等待之前将定时器分辨率设置为1 ms以便准确(无论睡眠长度如何),并在完成后立即恢复它,除非任何其他线程仍处于休眠状态。然后,这个设置/重置将混淆Windows时钟,它希望窗口时间量相当恒定。
Sun实际上known about this since 2006,并没有修复它,AFAICT!
由于这个原因,我们实际上时钟速度提高了两倍!一个简单的Java程序在循环中睡眠1毫秒就会出现这种情况。
解决方案是将自己的时间分辨率设置为低,并尽可能长时间保持在那里。使用timeBeginPeriod()来控制它。 (我们将其设置为1毫秒,没有任何不利影响。)
对于那些用Java编码的人来说,更容易解决这个问题的方法是创建一个只要应用程序存在就会休眠的线程。
请注意,无论哪个应用程序是真正的罪魁祸首,这都将在全局机器上解决此问题。
答案 2 :(得分:5)
您可以在计划任务.bat文件中运行“w32tm / resync”。这适用于Windows Server 2003。
答案 3 :(得分:4)
除了更频繁地重新同步时钟之外,我认为除了获得一块新主板之外你还有很多工作要做,因为你的时钟信号似乎没有达到合适的频率。
答案 4 :(得分:2)
如前所述,Java程序可能会导致此问题。
另一个不需要修改代码的解决方案是添加VM参数-XX:+ForceTimeHighResolution
(在NTP support page上找到)。
9.2.3。 Windows和Sun的Java虚拟机
Sun的Java虚拟机需要使用> -XX:+ ForceTimeHighResolution参数启动,以避免丢失中断。
有关详细信息,请参阅http://www.macromedia.com/support/coldfusion/ts/documents/createuuid_clock_speed.htm。
从引用的链接(通过Wayback machine - 原始链接消失):
ColdFusion MX:CreateUUID提高Windows系统时钟速度
在加载时多次调用createUUID函数 Macromedia ColdFusion MX及更高版本可能会导致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函数具有内部Thread.sleep 呼叫1毫秒。当大量使用createUUID时, Windows系统时钟每分钟会增加几秒钟。以...的速率 加速度与createUUID调用的数量成正比 加载ColdFusion MX服务器。 Macromedia已经观察到这一点 在Windows XP,2000和2003上的ColdFusion MX及更高版本中的行为 系统
答案 5 :(得分:2)
http://www.codinghorror.com/blog/2007/01/keeping-time-on-the-pc.html
PC时钟通常应该精确到每天几秒钟。如果你遇到大量的时钟漂移 - 每天几分钟 - 首先要检查的是你的交流电源。我个人观察到UPS连接到另一台UPS(顺便说一句,这是禁止的),每天只需几分钟。从链中移除不必要的UPS修复了时间问题。我不是硬件工程师,但我猜测电源中的一些定时信号被主板上的实时时钟芯片使用。
答案 6 :(得分:1)
增加重新同步的频率。 如果同步与您自己网络上的主服务器同步,则没有理由不在每分钟同步。
答案 7 :(得分:1)
更频繁地同步。查看Registry entries for the W32Time service,尤其是“期间”。 “SpecialSkew”听起来会对你有所帮助。
答案 8 :(得分:1)
时钟漂移可能是温度的结果;也许你可以尝试让温度更稳定 - 也许使用更好的冷却?不过,你永远不会完全失去漂移。
使用外部时钟(GPS接收器等),以及将CPU时间与绝对时间相关联的统计方法,我们在此处用于在分布式系统中同步事件。
答案 9 :(得分:1)
因为听起来你有一个大企业:
拿一台旧笔记本电脑或其他不太好的东西,但似乎有一个或多或少可靠的时钟,并称之为计时器。计时员的唯一工作是,每隔(比如说)2分钟,向服务器发送一条消息,告诉时间。服务器不会使用Windows时钟作为时间戳,而是将计时器的最后一个信号加上自信号以来经过的时间。每周一次或两次用手表检查计时器的时钟。这应该足够了。
答案 10 :(得分:0)
你在运行什么服务器?在台式机中,我遇到这种情况的时候是启用了Spread Spectrum FSB,导致中断时序出现问题,这就是时钟滴答的原因。可能想看看这是其中一台服务器的BIOS选项,如果启用则关闭它。
你有另一个选择是编辑时间轮询间隔并使用以下注册表项缩短它,很可能你必须添加它(注意这是一个DWORD值,值是以秒为单位,例如600 10分钟):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient\SpecialPollInterval
以下是完整的测试: KB816042
答案 11 :(得分:0)
我曾经写过一个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.
答案 12 :(得分:0)
我相信Windows时间服务只实现SNTP,这是NTP的简化版本。完整的NTP实现考虑了时钟在决定同步频率方面的稳定性。