On this page

Clocks and Timers

Coverage: timer wheel → hrtimer (Red-Black Tree) → clockevent/clocksource → tick (periodic/dyntick/nohz) → POSIX timer → itimer Kernel version: 2.6 ~ 6.x

Overview

The kernel has two timer systems coexisting:

  • timer wheel (low-precision): jiffy granularity (1/HZ seconds, typically 1ms/4ms), mod_timer()/del_timer()
  • hrtimer (high-precision): nanosecond level, sorted by Red-Black Tree, hrtimer_start()/hrtimer_cancel()

This dual-track design stems from history: the timer wheel is the original implementation, while hrtimer is a precise alternative introduced in 2.6.16. Both now coexist—the timer wheel handles low-precision, high-volume timers (such as network timeouts), while hrtimer handles precise events (such as POSIX timers and tick simulation).


Jiffies and HZ

// include/linux/jiffies.h
extern unsigned long volatile jiffies;  // Number of ticks since boot

// HZ = number of ticks per second (configured at kernel compile time):
//   CONFIG_HZ=100   → 10ms precision (servers, low CPU overhead)
//   CONFIG_HZ=250   → 4ms precision (default)
//   CONFIG_HZ=1000  → 1ms precision (desktop/real-time, smoother but more interrupts)

// jiffies wrap:
//   32-bit: 2^32 / HZ ≈ 497 days (HZ=100)
//   64-bit: wrap exceeds the age of the universe
//   time_after(a, b) / time_before(a, b) macros handle wrap comparisons

Timer Wheel: Low-Precision Timers

// kernel/time/timer.c
// Buckets based on expiration time (similar to a water clock)
// Bucket sizes: 1 jiffy, 8 jiffies, 64 jiffies, 512 jiffies, ...

struct timer_list {
    unsigned long       expires;      // Expiration jiffies
    void                (*function)(struct timer_list *);
    u32                 flags;
};

// Registration:
mod_timer(&timer, jiffies + msecs_to_jiffies(500)); // Trigger after 500ms

// Deletion:
del_timer(&timer);         // Synchronous deletion
del_timer_sync(&timer);    // Wait for potentially running handler to complete

// Timers run in softirq context (TIMER_SOFTIRQ)
// → Cannot sleep, cannot acquire mutexes

hrtimer: High-Precision Timers

// kernel/time/hrtimer.c
// Nanosecond precision, sorted by Red-Black Tree
// Requires hardware support (HPET, TSC, or APIC timer)

struct hrtimer {
    ktime_t             expires;        // Absolute expiration time (ktime_t, ns)
    enum hrtimer_restart (*function)(struct hrtimer *);
};

// Usage:
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
timer.function = my_callback;
hrtimer_start(&timer, ns_to_ktime(1000000), HRTIMER_MODE_REL);
// → Callback after 1ms

// hrtimer expiration → callback in interrupt context (HRTIMER_SOFTIRQ)
// If sleeping is needed → use schedule_hrtimeout()

Clock Event and Clock Source

// kernel/time/clockevents.c + kernel/time/clocksource.c
// clocksource: Provides current time (abstracts TSC, HPET, ACPI PM timer)
//   read() → returns cycle_t (monotonic counter)
//   → Converted to nanoseconds via mult/shift

// clockevent: Provides future event programming (abstracts Local APIC timer, HPET)
//   set_next_event(delta_ns) → Programs hardware to generate an interrupt after delta_ns
//   → This is the hardware basis for ticks and hrtimers

// tick device:
//   Each CPU has a clockevent acting as a tick device
//   Can be per-CPU (Local APIC timer) or global broadcast (HPET)

Tick Management: periodic / dyntick / nohz

// Three tick modes:

// 1. Periodic tick (legacy):
//    Each CPU generates a timer interrupt every 1/HZ seconds
//    → Used to update time (jiffies), scheduler ticks, and process statistics
//    → However, most ticks are wasted (ticks are not needed when CPU is idle)

// 2. Dynamic tick (dyntick / CONFIG_NO_HZ_IDLE, 2.6.21+):
//    When CPU is idle: stop tick → CPU enters deep sleep (C-state)
//    → Program clockevent for a one-time wake-up based on the next timer expiration
//    → Energy saving: more work per watt

// 3. Full nohz (CONFIG_NO_HZ_FULL, 3.10+):
//    When CPU has only one runnable task: stop tick
//    → Reduces tick interrupt interference with latency-sensitive tasks (HPC/real-time)
//    → However, RCU and scheduler statistics require adaptation → more complex

POSIX Timer and itimer

// kernel/time/posix-timers.c
// User-space timer API:

// POSIX timer (per-process):
timer_create(CLOCK_MONOTONIC, &evp, &timerid);
timer_settime(timerid, 0, &its, NULL);
// → Kernel creates hrtimer → Sends signal upon expiration (SIGALRM, SIGEV_SIGNAL)

// POSIX interval timer (itimer):
setitimer(ITIMER_REAL, &value, NULL);  // SIGALRM
setitimer(ITIMER_VIRTUAL, &value, NULL);  // SIGVTALRM (user-space time only)
setitimer(ITIMER_PROF, &value, NULL);  // SIGPROF (user + kernel time)

// timerfd (2.6.25+):
int fd = timerfd_create(CLOCK_MONOTONIC, 0);
timerfd_settime(fd, 0, &its, NULL);
// → fd becomes readable → timer expired (can be used with poll/epoll, very convenient)

Debugging

# Current tick mode
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/disable

# Timer wheel statistics (/proc/timer_list)
cat /proc/timer_list | head -100

# hrtimer statistics
cat /proc/timer_list | grep -A5 hrtimer

# HZ value
getconf CLK_TCK          # User-space HZ
grep CONFIG_HZ /boot/config-$(uname -r)  # Kernel compile-time HZ

References and Further Reading

  • Kernel Documentation: Documentation/timers/
  • LWN: "The timer wheel", "hrtimers and the tick", "NOHZ full"
  • Source Code:
    • kernel/time/timer.c — timer wheel
    • kernel/time/hrtimer.c — hrtimer
    • kernel/time/tick-common.c — tick management
    • kernel/time/tick-sched.c — dyntick/nohz
    • kernel/time/clocksource.c — clocksource
    • kernel/time/clockevents.c — clockevent
    • kernel/time/posix-timers.c — POSIX timer

Keywords: timer wheel, hrtimer, jiffies, HZ, clockevent, clocksource, dyntick, nohz, POSIX timer, timerfd