web-dev-qa-db-ja.com

Windowsでのクロックドリフト

ビジネスイベントを追跡するWindowsサービスを開発しました。 Windowsクロックを使用してイベントにタイムスタンプを付けます。ただし、特にCPUが正常に動作している場合、基になるクロックは非常に劇的にドリフトする可能性があります(たとえば、1分あたり数秒の損失)。私たちのサーバーは、Windowsタイムサービスを使用してドメインコントローラーとの同期を維持します。ドメインコントローラーは内部でNTPを使用しますが、同期頻度はドメインポリシーによって制御され、いずれの場合も1分ごとに同期します。ハードウェアクロックを使用する以外に、クロックをより安定させるために使用できる手法はありますか?

15
Matt Howells

クロックティックは予測可能である必要がありますが、ほとんどのPCハードウェアでは-なぜならはリアルタイムシステム用に設計されていないためです-他のI/Oデバイス割り込みが優先されますクロックティック割り込みを介して、一部のドライバは、割り込みサービスルーチンでそれを 遅延プロシージャコール (DPC)に延期するのではなく、広範な処理を実行します。これは、システムがクロックティックを処理できない可能性があることを意味します。信号が送信されてから(場合によっては)長くなるまで中断します。

その他の要因には、CPUから多くのメモリバスサイクルを盗むバスマスタリングI/Oコントローラが含まれ、CPUのメモリバス帯域幅がかなりの期間不足します。

他の人が言っているように、クロック生成ハードウェアは、コンポーネントの値が温度とともに変化するにつれて、その周波数も変化する可能性があります。

Windowsでは、すべての割り込みでリアルタイムクロックに追加されるティックの量を調整できます。SetSystemTimeAdjustmentを参照してください。ただし、これは、予測可能なクロックスキューがある場合にのみ機能します。時計がわずかにずれている場合、SNTPクライアント(「WindowsTime」サービス)はこのスキューを調整して、時計の刻みをわずかに速くしたり遅くしたりして、正しい時刻に向かう傾向を示します。

17
Mike Dimmick

これが当てはまるかどうかはわかりませんが...

Windowsには、タイマーの解像度を timeBeginPeriod() で大幅に変更すると、クロックがドリフトするという問題があります。

実際、Javaのスレッドwait()(およびos::sleep())関数のWindows実装には、この動作を引き起こすバグがあります。正確にするために(スリープの長さに関係なく)待機する前に常にタイマーの解像度を1ミリ秒に設定し、他のスレッドがまだスリープしていない限り、完了後すぐに復元します。このセット/リセットにより、Windowsクロックが混乱し、Windowsのタイムクォンタムがかなり一定になることが期待されます。

Sunは実際に 2006年以来これについて知られています 、そしてそれを修正していません、AFAICT!

このため、実際には時計が2倍速くなりました。ループで1ミリ秒スリープする単純なJavaプログラムは、この動作を示しています。

解決策は、時間分解能を自分で低く設定し、可能な限りそこに維持することです。 timeBeginPeriod()を使用してそれを制御します。 (悪影響なしに1ミリ秒に設定しました。)

Javaでコーディングしている場合、これを修正する簡単な方法は、アプリが存続している間スリープするスレッドを作成することです。

これにより、実際の原因がどのアプリケーションであるかに関係なく、マシン上のこの問題がグローバルに修正されることに注意してください。

12
Macke

スケジュールされたタスクの.batファイルで「w32tm/resync」を実行できます。これはWindowsServer2003で機能します。

5
Larry Silverman

クロックをより頻繁に再同期する以外に、新しいマザーボードを入手する以外にできることはあまりないと思います。クロック信号が適切な周波数になっていないようです。

4
TraumaPony

すでに述べたように、Javaプログラムがこの問題を引き起こす可能性があります。

コードの変更を必要としない別の解決策は、VM引数-XX:+ForceTimeHighResolutionNTPサポートページ にあります)を追加することです。

9.2.3。 WindowsとSunのJava仮想マシン

SunのJava仮想マシンは、割り込みが失われないように、>-XX:+ ForceTimeHighResolutionパラメーターで起動する必要があります。

詳細については、 http://www.macromedia.com/support/coldfusion/ts/documents/createuuid_clock_speed.htm を参照してください。

参照されたリンクから( ウェイバックマシン -元のリンクがなくなった):

ColdFusion MX:CreateUUIDはWindowsシステムのクロック速度を向上させます

Macromedia ColdFusion MX以降で負荷がかかった状態でcreateUUID関数を複数回呼び出すと、Windowsシステムのクロックが加速する可能性があります。これはJava仮想マシン(JVM)の問題であり、Thread.sleepの呼び出しが10ミリ秒(ms)未満の場合、Windowsシステムクロックの実行速度が速くなります。この動作は元々Sunとして提出されました。 Javaバグ4500388(developer.Java.Sun.com/developer/bugParade/bugs/4500388.html)であり、1.3.xおよび1.4.xJVMで確認されています。

ColdFusion MXでは、createUUID関数には1ミリ秒の内部Thread.sleep呼び出しがあります。 createUUIDが頻繁に使用される場合、Windowsシステムクロックは1分あたり数秒増加します。アクセラレーションの速度は、createUUID呼び出しの数とColdFusionMXサーバーの負荷に比例します。 Macromediaは、Windows XP、2000、および2003システムのColdFusionMX以降でこの動作を確認しています。

2
seeker

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

PCの時計は通常、1日あたり数秒以内の精度である必要があります。 1日あたり数分程度の大規模なクロックドリフトが発生している場合、最初に確認するのはAC電源です。私は、UPSが別のUPSに接続されているシステム(ちなみに、これはノーノーです)が1日あたり数分増加するのを個人的に観察しました。チェーンから不要なUPSを削除すると、時間の問題が修正されました。私はハードウェアエンジニアではありませんが、電源のタイミング信号の一部がマザーボード上のリアルタイムクロックチップによって使用されていると思います。

2
Sam Greenhalgh

再同期の頻度を増やします。同期が独自のネットワーク上の独自のメインサーバーと行われている場合、毎分同期しない理由はありません。

1
Martin Beckett

より頻繁に同期します。 W32Timeサービスのレジストリエントリ 、特に「期間」を見てください。 「SpecialSkew」はそれがあなたを助けるように聞こえます。

1
Tomalak

あなたは大企業を持っているように聞こえるので:

古いラップトップか、あまり良くないが、多かれ少なかれ信頼できる時計を持っているように見えるものを取り、それをタイムキーパーと呼びます。タイムキーパーの唯一の仕事は、(たとえば)2分ごとに1回、サーバーに時刻を知らせるメッセージを送信することです。サーバーは、タイムスタンプにWindowsクロックを使用する代わりに、タイムキーパーの最後のシグナルからの時間と、シグナルからの経過時間を記録します。週に1、2回、腕時計でタイムキーパーの時計を確認してください。これで十分です。

1
Robert L

クロックドリフトは温度の結果である可能性があります。おそらく、より良い冷却を使用して、温度をより一定にしようとすることができますか?ただし、ドリフトを完全に失うことは決してありません。

外部クロック(GPS受信機など)を使用し、CPU時間を絶対時間に関連付ける統計的手法を使用して、分散システムでイベントを同期します。

1
xtofl

Windows Time Serviceは、NTPの簡易バージョンであるSNTPのみを実装していると思います。完全なNTP実装では、同期の頻度を決定する際にクロックの安定性が考慮されます。

full NTP Windows用サーバーはこちら を取得できます。

0
cjm

私はかつて、時間の再同期を処理するためにDelphiクラスを作成しました。以下に貼り付けます。 Larry Silvermanが言及した「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.
0
JosephStyons

どのサーバーを実行していますか?デスクトップでは、これに遭遇したのはスペクトラム拡散FSBが有効になっているため、そのクロックを刻む原因となる割り込みタイミングに問題が発生します。これがオプションかどうかを確認したい場合があります。それらのサーバーの1つのBIOSで、有効になっている場合はオフにします。

もう1つのオプションは、時間ポーリング間隔を編集し、次のレジストリキーを使用してそれをはるかに短くすることです。ほとんどの場合、これを追加する必要があります(これは、DWORD値であり、値は秒単位です。たとえば、10分間で600)。 :

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

これが完全な精密検査です:KB816042

0
Nick Craver