On this page

Time Management

Coverage: timekeeping → CLOCK_MONOTONIC/CLOCK_REALTIME/CLOCK_BOOTTIME → NTP → PTP → vsyscall/vDSO → Hardware clocks such as TSC/HPET/ACPI Kernel versions: 2.6 ~ 6.x

Overview

Kernel time management answers three questions: "What time is it?", "How much time has passed?", and "When to wake up?". Although it seems simple, it involves the selection and arbitration of multiple clock sources, NTP time synchronization, and fast time access in user space.

The key concept is distinguishing between REALTIME (wall-clock time, adjustable by NTP, affected by leap seconds) and MONOTONIC (reliable measurement since boot, monotonically increasing).


Clock Types

// include/uapi/linux/time.h

// CLOCK_REALTIME: Wall-clock time (Unix epoch)
//   Adjusted by NTP, can jump backwards (leap seconds, settimeofday)
//   → Not suitable for delta measurements

// CLOCK_MONOTONIC: Time since boot
//   Monotonically increasing, not adjusted by NTP, does not jump backwards
//   → Suitable for measuring time intervals

// CLOCK_MONOTONIC_RAW: Raw hardware count
//   Not subject to NTP frequency adjustments (harder to distort than MONOTONIC)
//   → Suitable for precise benchmarking

// CLOCK_BOOTTIME: Includes suspend time
//   Similar to MONOTONIC, but includes system sleep time
//   → Suitable for scenarios requiring "true elapsed wall-clock time"

// CLOCK_TAI: International Atomic Time
//   Offset from UTC by 37 seconds (as of 2024)
//   Not affected by leap seconds

timekeeping Core

// kernel/time/timekeeping.c
// Core data structure:
struct timekeeper {
    struct clocksource          *clock;  // Currently selected clocksource
    u64                         cycle_interval;  // Cycles corresponding to NTP tick
    u64                         xtime_sec;       // REALTIME seconds
    unsigned long               xtime_nsec;      // REALTIME nanosecond part
    ktime_t                     offs_real;       // REALTIME offset (NTP adjusted)
    ktime_t                     offs_boot;       // BOOTTIME offset
    ktime_t                     offs_tai;        // TAI offset
    struct timespec64           wall_to_monotonic; // REALTIME → MONOTONIC conversion
    struct timespec64           total_sleep_time;  // Cumulative suspend time
};

// timekeeping_advance():
//   Called once per tick (or hrtimer interrupt)
//   1. Read clocksource
//   2. Calculate delta cycles → delta_ns
//   3. Accumulate delta_ns into xtime_nsec (handling carry)
//   4. Apply NTP frequency correction (mult adjustment)
//   5. Update all derived times (MONOTONIC, BOOTTIME, TAI)

NTP Time Synchronization

// kernel/time/ntp.c
// Kernel NTP daemon:
//   Not the user-space ntpd/chronyd — those are controllers
//   The kernel NTP code performs the actual frequency and phase adjustments

// adjtimex() system call:
//   User-space ntpd tells the kernel via adjtimex:
//     1. Current offset (phase error, in nanoseconds)
//     2. Current freq (frequency error, in PPM)
//     3. Maximum error
//   Kernel: Adjusts timekeeper->mult (frequency) and offset (phase)

// PTP (Precision Time Protocol):
//   Linux supports hardware timestamps via SO_TIMESTAMPING
//   PHC (PTP Hardware Clock) drivers → /dev/ptp*
//   → Nanosecond-level time synchronization (data centers/financial trading)

vDSO: Fast Time Access

// arch/x86/entry/vdso/
// Problem: If clock_gettime() uses a syscall → ~100ns per call (syscall + context switch)
//       High-frequency calls (e.g., profiling) incur excessive overhead

// vDSO (virtual Dynamic Shared Object):
//   The kernel injects a small piece of code into each process's address space
//   → clock_gettime() executes in user space → bypasses the kernel!
//   → Reads clocksource, applies timekeeper's offset and mult → returns time
//   → Latency: ~20ns (vs ~100ns for syscall)

// vsyscall (legacy, deprecated) vs vDSO (modern):
//   vsyscall: Fixed address, security risk (prone to ROP attacks)
//   vDSO:    ASLR random address, standard ELF .so

x86 vDSO Implementation

// arch/x86/entry/vdso/vclock_gettime.c
// clock_gettime(CLOCK_MONOTONIC) in user space:
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) {
    // 1. Read TSC (RDTSC instruction, ~10 cycles)
    cycles = __arch_counter_get_cntvct();

    // 2. Apply clocksource conversion (in vvar page)
    ns = (cycles - vd->cycle_last) * vd->mult + vd->xtime_nsec;

    // 3. Handle seqlock consistency
    //    vd->seq is a sequence number that goes even→odd→even
    //    Record seq before reading, verify seq hasn't changed after reading → data consistent
}

Hardware Clock Sources

Clock SourcePrecisionLatencyStabilityScenario
TSC (rdtsc)~0.3ns~10 cyclesHigh (constant/invariant TSC)Default preferred
HPET~100ns~500 cyclesHighLegacy systems or when TSC is unstable
ACPI PM Timer~280ns~1μsMediumSame as above
ARM Generic Timer~10ns~10 cyclesHighARM64 default
KVM pvclock~20ns~200 cyclesGuaranteed by hostVirtualization
# View the currently used clocksource
cat /sys/devices/system/clocksource/clocksource0/current_clocksource

# View available list
cat /sys/devices/system/clocksource/clocksource0/available_clocksource

References and Further Reading

  • Kernel Documentation: Documentation/timers/timekeeping.rst
  • LWN: "The vDSO and time", "Timekeeping in the Linux kernel"
  • Source Code:
    • kernel/time/timekeeping.c — timekeeping core
    • kernel/time/ntp.c — NTP
    • arch/x86/entry/vdso/ — x86 vDSO
    • arch/arm64/kernel/vdso/ — ARM64 vDSO
    • kernel/time/clocksource.c — clocksource management

Keywords: CLOCK_MONOTONIC, CLOCK_REALTIME, timekeeper, NTP, PTP, vDSO, TSC, HPET, clocksource