---
title: 时间管理
url: https://doc.liz6.com/linux-kernel/04-interrupts-and-clocks/03-time-management
locale: zh
area: linux-kernel
tags:
- linux-kernel
- 中断与时钟
date: 2026-06-30
modified: 2026-06-30
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

## 概述

内核时间管理回答三个问题："现在几点？"、"过了多久？"、"什么时候唤醒？"。看似简单，但涉及多个时钟源的选择与仲裁、NTP 校时、以及用户空间的快速时间访问。

关键概念是区分 **REALTIME** (挂钟时间，可被 NTP 调整，受闰秒影响) 和 **MONOTONIC** (自启动以来的可靠测量，单调递增)。

---

## 时钟类型

```c
// include/uapi/linux/time.h

// CLOCK_REALTIME: 挂钟时间 (Unix epoch)
//   受 NTP 调整, 可以往回跳 (闰秒, settimeofday)
//   → 不适合做 delta 测量

// CLOCK_MONOTONIC: 自启动以来的时间
//   单调递增, 不受 NTP 调整, 不会往回跳
//   → 适合测量时间间隔

// CLOCK_MONOTONIC_RAW: 原始硬件计数
//   不受 NTP 频率调整 (比 MONOTONIC 更难被扭曲)
//   → 适合精确基准测试

// CLOCK_BOOTTIME: 包含 suspend 时间
//   与 MONOTONIC 类似，但包含系统睡眠的时间
//   → 适合需要"墙上的真实流逝时间"的场景

// CLOCK_TAI: International Atomic Time
//   与 UTC 偏移 37 秒 (截至 2024)
//   不受闰秒影响
```

---

## timekeeping 核心

```c
// kernel/time/timekeeping.c
// 核心数据结构:
struct timekeeper {
    struct clocksource          *clock;  // 当前选中的 clocksource
    u64                         cycle_interval;  // NTP tick 对应的 cycles
    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;  // 累计 suspend 时间
};

// timekeeping_advance():
//   每个 tick (或 hrtimer interrupt) 调用一次
//   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 daemon:
//   不是用户空间的 ntpd/chronyd — 那是控制者
//   内核 NTP 代码执行实际的频率和相位调整

// adjtimex() 系统调用:
//   用户空间 ntpd 通过 adjtimex 告诉内核:
//     1. 当前 offset (相位误差, 多少纳秒)
//     2. 当前 freq (频率误差, 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 → 每次 ~100ns (syscall + context switch)
//       高频调用 (如 profiling) 开销太大

// vDSO (virtual Dynamic Shared Object):
//   内核把一小段代码注入每个进程的地址空间
//   → clock_gettime() 在用户态执行 → 不经过内核!
//   → 读 clocksource, 应用 timekeeper 的偏移和 mult → 返回时间
//   → 延迟: ~20ns (vs ~100ns for syscall)

// vsyscall (旧方式, 已废弃) vs 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)
    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 周期 | 由 host 保证 | 虚拟化 |

```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*
