---
title: 時間管理
url: https://doc.liz6.com/ja/linux-kernel/04-interrupts-and-clocks/03-time-management
locale: ja
area: linux-kernel
tags:
- linux-kernel
- interrupts-and-clocks
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: timekeeping → CLOCK_MONOTONIC/CLOCK_REALTIME/CLOCK_BOOTTIME → NTP → PTP → vsyscall/vDSO → TSC/HPET/ACPI などのハードウェアクロック。カーネルバージョン: 2.6 ~ 6.x'
---

# 時間管理

> カバー範囲: timekeeping → CLOCK_MONOTONIC/CLOCK_REALTIME/CLOCK_BOOTTIME → NTP → PTP → vsyscall/vDSO → TSC/HPET/ACPI などのハードウェアクロック
> カーネルバージョン: 2.6 ~ 6.x

## 概要

カーネルの時間管理は、以下の3つの問いに答えます。「今何時か？」「どれだけの時間が経過したか？」「いつウェイクアップするか？」。一見単純に見えますが、複数のクロックソースの選択と仲裁、NTP による時刻合わせ、およびユーザー空間での高速な時間アクセスが含まれます。

重要な概念は、**REALTIME**（壁掛け時計の時刻、NTP によって調整可能、うるう秒の影響を受ける）と **MONOTONIC**（起動からの信頼性の高い測定、単調増加）を区別することです。

---

## クロックの種類

```c
// 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 コア

```c
// kernel/time/timekeeping.c
// 主要なデータ構造:
struct timekeeper {
    struct clocksource          *clock;  // 現在選択されている clocksource
    u64                         cycle_interval;  // NTP tick に対応するサイクル数
    u64                         xtime_sec;       // REALTIME の秒数
    unsigned long               xtime_nsec;      // REALTIME のナノ秒部分
    ktime_t                     offs_real;       // REALTIME のオフセット (NTP 調整)
    ktime_t                     offs_boot;       // BOOTTIME のオフセット
    ktime_t                     offs_tai;        // TAI のオフセット
    struct timespec64           wall_to_monotonic; // REALTIME → MONOTONIC への変換
    struct timespec64           total_sleep_time;  // 累積サスペンド時間
};

// 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 による時刻合わせ

```c
// 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: 高速な時間アクセス

```c
// 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 実装

```c
// arch/x86/entry/vdso/vclock_gettime.c
// ユーザー空間での clock_gettime(CLOCK_MONOTONIC):
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) {
    // 1. TSC を読み取る (RDTSC 命令、約10サイクル)
    cycles = __arch_counter_get_cntvct();

    // 2. clocksource 変換を適用する (vvar ページ内)
    ns = (cycles - vd->cycle_last) * vd->mult + vd->xtime_nsec;

    // 3. seqlock の整合性を処理する
    //    vd->seq は 偶→奇→偶 のシーケンス番号
    //    読み取り前に seq を記録し、読み取り後に seq が変わっていないことを検証 → データ整合性
}
```

---

## ハードウェアクロックソース

| クロックソース | 精度 | レイテンシ | 安定性 | シナリオ |
|--------|------|------|--------|--------|
| 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サイクル | ホストに依存 | 仮想化環境 |

```bash
# 現在使用されている clocksource を表示
cat /sys/devices/system/clocksource/clocksource0/current_clocksource

# 利用可能なリストを表示
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
```

---

## 参照と拡張

- **カーネルドキュメント**: `Documentation/timers/timekeeping.rst`
- **LWN**: "The vDSO and time", "Timekeeping in the Linux kernel"
- **ソースコード**:
  - `kernel/time/timekeeping.c` — timekeeping コア
  - `kernel/time/ntp.c` — NTP
  - `arch/x86/entry/vdso/` — x86 vDSO
  - `arch/arm64/kernel/vdso/` — ARM64 vDSO
  - `kernel/time/clocksource.c` — clocksource 管理

---

*キーワード: CLOCK_MONOTONIC, CLOCK_REALTIME, timekeeper, NTP, PTP, vDSO, TSC, HPET, clocksource*
