ビジネス イベントを追跡する Windows サービスを開発しました。イベントのタイムスタンプに Windows クロックを使用します。ただし、基礎となるクロックは、特に CPU がハードに動作している場合に、非常に劇的にドリフトする可能性があります (たとえば、1 分間に数秒を失うなど)。私たちのサーバーは Windows タイム サービスを使用して、内部で NTP を使用するドメイン コントローラーとの同期を維持しますが、同期の頻度はドメイン ポリシーによって制御されます。ハードウェア クロックを使用する以外に、クロックをより安定させるために使用できる手法はありますか?
13 に答える
クロック ティックは予測可能ですが、ほとんどの PC ハードウェアでは (リアルタイム システム用に設計されていないため)、他の I/O デバイス割り込みがクロック ティック割り込みよりも優先され、一部のドライバーは割り込みサービス ルーチンで広範な処理を行います。遅延プロシージャ コール(DPC) に延期するよりも、システムがクロック ティック割り込みを処理できない可能性があることを意味します。
その他の要因には、CPU から多くのメモリ バス サイクルを盗むバス マスタリング I/O コントローラが含まれます。これにより、かなりの期間、メモリ バス帯域幅が不足します。
他の人が言ったように、クロック生成ハードウェアは、コンポーネントの値が温度によって変化するにつれて、その周波数も変化する可能性があります。
Windows では、割り込みごとにリアルタイム クロックに追加されるティックの量を調整できます。SetSystemTimeAdjustment を参照してください。ただし、これは予測可能なクロック スキューがある場合にのみ機能します。クロックがわずかにずれている場合、SNTP クライアント (「Windows タイム」サービス) はこのスキューを調整して、クロックの刻みをわずかに速くしたり遅くしたりして、正しい時間に近づけます。
これが当てはまるかどうかはわかりませんが...
Windowsには、 timeBeginPeriod()を使用してタイマーの解像度を大幅に変更すると、時計がずれてしまうという問題があります。
実際、Javaのスレッドwait()
(およびos::sleep()
)関数のWindows実装には、この動作を引き起こすバグがあります。(スリープの長さに関係なく)正確にするために、待機する前に常にタイマーの解像度を1ミリ秒に設定し、他のスレッドがまだスリープしていない限り、完了後すぐに復元します。このセット/リセットは、Windowsクロックを混乱させます。これは、Windowsのタイムクォンタムがかなり一定であることを期待しています。
Sunは2006年以来これについて実際に知っていて、それを修正していません、AFAICT!
このため、実際には時計が2倍速くなりました。ループで1ミリ秒スリープする単純なJavaプログラムは、この動作を示しています。
解決策は、時間分解能を自分で低く設定し、可能な限りそこに保持することです。timeBeginPeriod()を使用してそれを制御します。(悪影響なしに1ミリ秒に設定しました。)
Javaでコーディングしている場合、これを修正する簡単な方法は、アプリが存続している間スリープするスレッドを作成することです。
これにより、実際の原因がどのアプリケーションであるかに関係なく、マシン上のこの問題がグローバルに修正されることに注意してください。
スケジュールされたタスクの.batファイルで「w32tm/resync」を実行できます。これはWindowsServer2003で機能します。
クロックをより頻繁に再同期する以外に、クロック信号が適切な周波数にないように見えるため、新しいマザーボードを入手する以外にできることはあまりないと思います。
http://www.codinghorror.com/blog/2007/01/keeping-time-on-the-pc.html
PC の時計は通常、1 日あたり数秒以内の精度である必要があります。1 日に数分程度の大きなクロック ドリフトが発生している場合は、まず AC 電源を確認してください。私は個人的に、UPS が別の UPS に接続されているシステムを観察しました (ちなみに、これはノーノーです)。チェーンから不要な UPS を削除すると、時間の問題が修正されました。私はハードウェア エンジニアではありませんが、電源のタイミング信号がマザーボード上のリアルタイム クロック チップによって使用されていると推測しています。
すでに述べたように、Java プログラムがこの問題を引き起こす可能性があります。
コードの変更を必要としない別の解決策は、VM 引数を追加することです-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 システム クロックは 1 分あたり数秒増加します。加速率は、createUUID 呼び出しの数と ColdFusion MX サーバーの負荷に比例します。Macromedia では、Windows XP、2000、および 2003 システムの ColdFusion MX 以降でこの動作を確認しています。
クロックドリフトは温度の結果である可能性があります。おそらく、より良い冷却を使用して、温度をより一定にしようとすることができますか?ただし、ドリフトを完全に失うことはありません。
外部クロック(GPS受信機など)と、CPU時間を絶対時間に関連付ける統計的手法を使用して、分散システムでイベントを同期するためにここで使用します。
あなたは大きなビジネスをしているように聞こえるので:
古いラップトップなど、あまり役に立たないが、多かれ少なかれ信頼性の高い時計を備えているように見えるものを取り上げ、それを Timekeeper と呼びます。Timekeeper の唯一の仕事は、(たとえば) 2 分ごとにサーバーにメッセージを送信して時刻を伝えることです。タイムスタンプに Windows クロックを使用する代わりに、サーバーはタイムキーパーの最後の信号からの時間と、信号からの経過時間を記録します。週に 1 ~ 2 回、腕時計でタイムキーパーの時計を確認してください。これで十分です。
再同期の頻度を増やします。同期が独自のネットワーク上の独自のメイン サーバーとの間で行われる場合、毎分同期しない理由はありません。
より頻繁に同期します。W32Time サービスのレジストリ エントリ、特に「期間」を調べます。「SpecialSkew」が役に立ちそうです。
どのサーバーを実行していますか?デスクトップでは、これに遭遇したのはスペクトラム拡散FSBが有効になっている場合であり、割り込みタイミングにいくつかの問題が発生し、それがそのクロックを刻みます。これがこれらのサーバーの1つにあるBIOSのオプションであるかどうかを確認し、有効になっている場合はオフにすることをお勧めします。
もう1つのオプションは、時間ポーリング間隔を編集し、次のレジストリキーを使用して大幅に短くすることです。ほとんどの場合、これを追加する必要があります(これは、DWORD値であり、値は秒単位です。たとえば、10分間は600です)。 :
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient\SpecialPollInterval
これが完全な精密検査です:KB816042
私はかつて、時間の再同期を処理する 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.
Windows タイム サービスは、NTP の簡略化されたバージョンである SNTP のみを実装していると思います。完全な NTP 実装では、同期する頻度を決定する際にクロックの安定性が考慮されます。
Windows 用の完全な NTP サーバーはこちらから入手できます。