このページの目次
時間管理
カバー範囲: timekeeping → CLOCK_MONOTONIC/CLOCK_REALTIME/CLOCK_BOOTTIME → NTP → PTP → vsyscall/vDSO → TSC/HPET/ACPI などのハードウェアクロック カーネルバージョン: 2.6 ~ 6.x
概要
カーネルの時間管理は、以下の3つの問いに答えます。「今何時か?」「どれだけの時間が経過したか?」「いつウェイクアップするか?」。一見単純に見えますが、複数のクロックソースの選択と仲裁、NTP による時刻合わせ、およびユーザー空間での高速な時間アクセスが含まれます。
重要な概念は、REALTIME(壁掛け時計の時刻、NTP によって調整可能、うるう秒の影響を受ける)と MONOTONIC(起動からの信頼性の高い測定、単調増加)を区別することです。
クロックの種類
// include/uapi/linux/time.h
// CLOCK_REALTIME: 壁掛け時計の時刻 (Unix エポック)
// NTP によって調整され、過去に戻ることがある (うるう秒、settimeofday)
// → delta 測定向けではない
// CLOCK_MONOTONIC: 起動からの時刻
// 単調増加し、NTP による調整を受けず、過去に戻らない
// → 時間間隔の測定に適している
// CLOCK_MONOTONIC_RAW: 生ハードウェアカウント
// NTP による周波数調整の影響を受けない (MONOTONIC よりも歪められにくい)
// → 精密なベンチマークに適している
// CLOCK_BOOTTIME: サスペンド時間を含む
// MONOTONIC と似ているが、システムのサスペンド時間を含む
// → 「壁掛け時計としての実際の経過時間」が必要なシナリオに適している
// CLOCK_TAI: 国際原子時
// UTC と 37 秒のオフセットがある (2024年時点)
// うるう秒の影響を受けない
timekeeping コア
// kernel/time/timekeeping.c
// 主要なデータ構造:
;
// timekeeping_advance():
// 各 tick (または hrtimer 割り込み) ごとに1回呼び出される
// 1. clocksource を読み取る
// 2. delta cycles → delta_ns を計算する
// 3. delta_ns を xtime_nsec に累積する (繰り上げを処理)
// 4. NTP 周波数補正を適用する (mult 調整)
// 5. すべての派生時間 (MONOTONIC, BOOTTIME, TAI) を更新する
NTP による時刻合わせ
// kernel/time/ntp.c
// カーネル NTP デーモン:
// ユーザー空間の ntpd/chronyd ではない — これらは制御側である
// カーネルの NTP コードが実際の周波数および位相の調整を実行する
// adjtimex() システムコール:
// ユーザー空間の ntpd は adjtimex を通じてカーネルに以下を指示する:
// 1. 現在のオフセット (位相誤差、ナノ秒単位)
// 2. 現在の周波数 (周波数誤差、PPM)
// 3. 最大誤差
// カーネル: timekeeper->mult (周波数) と offset (位相) を調整する
// PTP (Precision Time Protocol):
// Linux は SO_TIMESTAMPING を介してハードウェアタイムスタンプをサポートする
// PHC (PTP Hardware Clock) ドライバー → /dev/ptp*
// → ナノ秒単位の時間同期 (データセンター/金融取引向け)
vDSO: 高速な時間アクセス
// arch/x86/entry/vdso/
// 問題: clock_gettime() が syscall を使用する場合 → 1回あたり約100ns (syscall + コンテキストスイッチ)
// 高頻度呼び出し (プロファイリングなど) ではオーバーヘッドが大きすぎる
// vDSO (virtual Dynamic Shared Object):
// カーネルは各プロセスのアドレス空間に少量のコードを注入する
// → clock_gettime() がユーザー空間で実行される → カーネルを経由しない!
// → clocksource を読み取り、timekeeper のオフセットと mult を適用して時間を返す
// → レイテンシ: 約20ns (syscall の場合の約100nsに対して)
// vsyscall (旧方式、廃止済み) と vDSO (新方式):
// vsyscall: 固定アドレス、セキュリティリスク (ROP 攻撃が容易)
// vDSO: ASLR によるランダムアドレス、標準的な ELF .so
x86 vDSO 実装
// arch/x86/entry/vdso/vclock_gettime.c
// ユーザー空間での clock_gettime(CLOCK_MONOTONIC):
notrace int
ハードウェアクロックソース
| クロックソース | 精度 | レイテンシ | 安定性 | シナリオ |
|---|---|---|---|---|
| TSC (rdtsc) | 約0.3ns | 約10サイクル | 高い (constant/invariant TSC) | デフォルトの優先候補 |
| HPET | 約100ns | 約500サイクル | 高い | 旧式システムまたは TSC が不安定な場合 |
| ACPI PM Timer | 約280ns | 約1μs | 中程度 | 上記と同様 |
| ARM Generic Timer | 約10ns | 約10サイクル | 高い | ARM64 のデフォルト |
| KVM pvclock | 約20ns | 約200サイクル | ホストに依存 | 仮想化環境 |
# 現在使用されている clocksource を表示
# 利用可能なリストを表示
参照と拡張
- カーネルドキュメント:
Documentation/timers/timekeeping.rst - LWN: "The vDSO and time", "Timekeeping in the Linux kernel"
- ソースコード:
kernel/time/timekeeping.c— timekeeping コアkernel/time/ntp.c— NTParch/x86/entry/vdso/— x86 vDSOarch/arm64/kernel/vdso/— ARM64 vDSOkernel/time/clocksource.c— clocksource 管理
キーワード: CLOCK_MONOTONIC, CLOCK_REALTIME, timekeeper, NTP, PTP, vDSO, TSC, HPET, clocksource