クロック同期
概要
コンピュータにおけるクロック同期 (clock synchronization) とは、複数のコンピュータやデバイスがそれぞれ持つ内部時計をある特定の基準クロックに合わせることを指す。一般的なデバイスでは、単に時刻合わせが行われていない場合のみならず、その内部時計が時間の経過とともに基準時刻から徐々にずれて行くクロックドリフト (clock drift) という現象が発生する。クロック同期はそれらの誤差を基準時刻に合わせるプロセスである。
分散システムにおけるクロック同期は、ネットワークで接続された複数のノードがそれぞれの内部クロックを相互あるいは外部の共通時刻源に整合させるプロセスである。個々のノードが独立して動作する分散システムでは、各ノードのクロックドリフトの蓄積によってノード間で時刻がずれるクロックスキュー (clock skew) が生じる。そのため、システムの設計にはある程度のクロックスキュー耐性が必要となるが、その許容値を超えるほどクロックスキューが大きくなるとシステムの動作保証に関わる問題を引き起こす可能性がある。したがって、システムが正常に動作できる許容範囲内にクロックスキューを収めるために継続的なクロック同期が必要である。
また、アプリケーション設計はこのようなクロックスキューが常に存在することを前提とし、特に重要な処理においては安易にローカルクロックを使用すべきではない。例えば、金融システムにおいて契約締結や取引可能時間を厳密に扱うために、その基準時刻は必ず信用できるデータベースや勘定系ホストから取得するように設計されている。これは、各ノードのローカルクロックに依存することなく、それらを時刻源 (タイムサーバ) としてシステム全体で統一された時間を保証するためである。
Table of Contents
物理クロック
物理クロック (physical clock) は時間を計測するためにコンピュータやデバイスの内部に搭載されているハードウェアである。一般的に「クロック」と呼ばれているが実体は時計よりもタイマーに近い。一般的なプログラミング環境では RTC デバイスに基づく絶対時刻と、CPU クロックに基づく相対時刻の 2 つの時刻を使うことができる。
リアルタイムクロック
マザーボードや組み込み機器にしばしば搭載されている RTC (real-time clock) は、デバイスの電源がオフになっている間もバッテリーなどにより駆動し続け、システム時刻を保持・管理することで、起動時に正しい現在時刻を提供する独立した半導体回路である。RTC には水晶発振子 (quartz crystal oscillator) が組み込まれており、これは精密に加工された水晶の結晶に電圧をかけると非常に安定した周波数で振動する特性 (ピエゾ効果) を利用している。この水晶発振子からのパルス出力は複数段の分周回路 (prescaler) を経由して周波数が減らされ、最終的に 1 秒に 1 回の頻度で正確に CPU に割り込みを発生させることができる。
分周回路は水晶発振子の規則正しい振動をカウンター (counter) と保持レジスタ (holding register; または reload register) と呼ばれる 2 つのレジスタで計測する。一般に、水晶発振子を固有周波数 32,768Hz = 215Hz の周波数でパルスを出力させ、1 パルスごとにカウンターの値を 1 つ減らす。カウンターがゼロになると CPU に対して割り込みを発生し、保持レジスタに設定されている値でカウンターが上書きされる。この仕組みにより、保持レジスタの値によって任意の周波数で割り込みを発生させることができる [1]。この各割り込みをクロックティック (clock tick) と呼ぶ。
RTC はバッテリーでバックアップされた揮発性レジスタや不揮発性メモリに年月日時分秒のカレンダー情報を保持し (初期値は工場出荷時や OS インストール時、または NTP などの時刻同期などの外的な操作によって設定される)、割り込みを使って各フィールドを更新してゆく。ただし、RTC から現在時刻を読み出す操作はオーバーヘッドが大きく比較的遅い処理であるため、OS はシステム起動時に RTC から初期時刻を読み込み、それ以降は前述のクロックティックを数えることで OS 内部のシステムクロックを継続的に更新する。これによりアプリケーションからの高頻度な時刻参照に対して高速に応答できる。
クロックの精度は発振子や RTC のみならず温度変化や経年劣化の影響も受ける。そのため、同期を行っていないコンピュータの時刻は 1 ヶ月で ±80秒、1 日で ±2.5秒程度ずれると考えるのが妥当だろう。単一のコンピュータ内においては、すべてのプロセスは同じ時刻を共有しているため、クロックが少しずれても大きな問題にはならない。しかし、各々でクロックを持つ複数の CPU で構成される分散システムでは、クロックドリフトによって生じるクロックスキューがイベントの正確な時系列の整合性を損なう原因となるため、継続的なクロック同期を行う必要がある。
高分解能 CPU クロック
コンピュータが持つもう一つの物理クロックとして、CPU や SoC (system-on-a-chip) の内部タイマーが持つシステム起動からの経過時間がある。これを利用することで、たとえばタイムアウトの判定や処理にかかった時間の計測など、プログラム実行中に経過した時間を非常に高い精度 (一般的にはナノ秒) で計測することができる。
RTC に基づく時刻と異なり、CPU クロックに基づくタイマーは (ほとんどの環境で) 単調増加性 (monotonic) を保証しており、NTP 同期やユーザ操作でジャンプすることがない。ただし、CPU の電力変動や周波数変動、クロックソースの違いなどで若干のジッターが発生することがある。
絶対時刻と相対時刻
RTC ベースの絶対時刻は、ユーザ表示用や DB 保存時のタイムスタンプなど、今が何時何分何秒かを知りたいときに用いる。一方で、CPU クロックベースの相対時刻は、ある処理にかかった時間や、数秒後に必ず動かしたい処理、一定間隔で正確にループ処理したいといった外的な補正の影響を受けず経過時間を正確に追いたいときに使用すべきである。一般的なプログラミング環境では、これらの時刻を取得する機能が個別に用意されている (両方を組み合わせてナノ秒精度の高分解能時刻を利用できる環境もある)。
RTC ベースの時刻 (壁時計的) | CPU クロックベースの時刻 (相対タイマー) | |
---|---|---|
意味 | ハードウェア RTC チップや CMOS バックアップ時計が保持する実際の年月日時分秒 | CPU 内部や OS カーネルが持つ起動後からの経過時間 |
単調性 | なし。NTP 同期や手動操作、夏時間調整などで前後にジャンプする可能性あり。 | あり。基本的にシステム起動時から単調増加し、外部補正を受けない。 |
分解能 | ミリ秒~1秒 (チップやシステムに依存) | ナノ秒~マイクロ秒 (CPU タイマー/高分解能タイマーに依存) |
用途 |
|
|
C | time(&t) , gettimeofday(&tv, NULL) , clock_gettime(CLOCK_REALTIME, &ts) |
clock_gettime(CLOCK_MONOTONIC, &ts) , clock_gettime(CLOCK_MONOTONIC_RAW, &ts) |
Java | System.currentTimeMillis() , Instant.now() |
System.nanoTime() |
Rust | SystemTime::now() |
Instant::now() |
物理クロックの同期
物理クロック同期とは、コンピュータの内部にある時計 (リアルタイムクロックなど) を世界協定時刻 (UTC) のような現実世界の標準時刻に合わせることを目的とする。これは、イベントが発生した絶対的な時刻を正確に記録し、異なるシステム間や、システムと人間が時間を共有する上で必要なプロセスである。
Network Time Protocol
NTP (network time protocol) はインターネットで最も広く利用されている時刻同期プロトコルであり、その歴史はインターネットの黎明期まで遡る。NTP の主な目的は、ネットワークに接続されたコンピュータ間で数ミリ秒から数十ミリ秒オーダーの精度で時刻を同期することである。広域ネットワーク (WAN) のようなネットワーク地縁が変動しやすい環境下でも、高い信頼性と安定性をもって時刻情報を提供できるように設計されている。
PTP: Precision Time Protocol
論理クロックの同期
論理クロック (logical clock) はクロックと呼ばれているが時計を表してはいない。「分散システムで重要なのはそのイベントが何時何分に起きたかではなく、どの順序で起きたかである」
分散データベース
参考文献
- TANENBAUM, Andrew. VAN STEEN Maarten. 分散システム原理とパラダイム, 2002, 2.28: 1.