Kernel Tick
Up to µs
HIGH_RES
Why Aren’t Timers Always Exact?
When you set a timer for exactly 1 second, it might fire at 1.004s. This is not a bug — it is how OS scheduling works. The kernel only gets to check timers at each “tick” of its internal clock.
Understanding timer accuracy is essential for writing reliable time-sensitive programs.
Linux uses a software clock that ticks at a fixed rate called HZ. Each tick is a jiffy.
| HZ Value | Jiffy Duration | Typical System | Timer Rounding |
|---|---|---|---|
| 100 | 10 ms | Older kernels | Rounded to 10ms multiples |
| 250 | 4 ms | Default Linux desktop | Rounded to 4ms multiples |
| 1000 | 1 ms | RT / low-latency | Rounded to 1ms multiples |
| HIGH_RES | ~1 µs | Kernel ≥ 2.6.21 with CONFIG | Hardware-limited precision |
If jiffy = 4ms and you request a 19ms timer, it rounds UP to 20ms (the next jiffy boundary).
Timer value rounded up to next jiffy boundary. Fixed at setup time.
Process may not get CPU immediately after signal generation — other tasks may be running.
If signal is masked via
sigprocmask(), it is queued and delivered later when unblocked.With CONFIG_HIGH_RES_TIMERS enabled in the kernel, timer resolution is no longer limited by the jiffy. Hardware timers (HPET, TSC) are used directly.
- Resolution = jiffy size (1–10ms)
- setitimer / nanosleep limited
- Fine for most apps
- Resolution ~1 microsecond
- Hardware-limited accuracy
- Check via
clock_getres()
Use clock_getres() to discover whether high-res timers are available.
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <time.h>
int main(void) {
struct timespec res;
/* Check resolution of CLOCK_REALTIME */
clock_getres(CLOCK_REALTIME, &res);
printf("CLOCK_REALTIME resolution: %ld ns\n",
res.tv_sec * 1000000000L + res.tv_nsec);
/* Check resolution of CLOCK_MONOTONIC */
clock_getres(CLOCK_MONOTONIC, &res);
printf("CLOCK_MONOTONIC resolution: %ld ns\n",
res.tv_sec * 1000000000L + res.tv_nsec);
/* If resolution is 1ns, high-res timers are active.
If 1ms or more, standard jiffy-based timers are used. */
if (res.tv_nsec == 1 && res.tv_sec == 0)
printf("High-resolution timers ENABLED\n");
else
printf("Standard jiffy-based timers (resolution: %ld ms)\n",
(res.tv_sec * 1000L) + (res.tv_nsec / 1000000L));
return 0;
}
/* Compile: gcc -o check_res check_res.c -lrt
Sample output (high-res):
CLOCK_REALTIME resolution: 1 ns
CLOCK_MONOTONIC resolution: 1 ns
High-resolution timers ENABLED
*/
Measure the actual delay between requested and delivered timer expiry.
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
static struct timespec fired_at;
static volatile int got_alarm = 0;
void handler(int sig) {
/* Record time of signal delivery */
clock_gettime(CLOCK_MONOTONIC, &fired_at);
got_alarm = 1;
}
int main(void) {
struct sigaction sa;
struct itimerval itv;
struct timespec before, after;
long requested_us = 50000; /* 50 ms = 50000 us */
long actual_us;
sa.sa_handler = sigalrm_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = handler;
sigaction(SIGALRM, &sa, NULL);
/* Request 50ms one-shot timer */
itv.it_value.tv_sec = 0;
itv.it_value.tv_usec = requested_us;
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;
clock_gettime(CLOCK_MONOTONIC, &before);
setitimer(ITIMER_REAL, &itv, NULL);
while (!got_alarm)
pause();
actual_us = (fired_at.tv_sec - before.tv_sec) * 1000000L
+ (fired_at.tv_nsec - before.tv_nsec) / 1000L;
printf("Requested: %ld µs\n", requested_us);
printf("Actual : %ld µs\n", actual_us);
printf("Overrun : %ld µs\n", actual_us - requested_us);
return 0;
}
/* Example output (4ms jiffy system):
Requested: 50000 µs
Actual : 52000 µs
Overrun : 2000 µs (rounded up to next 4ms boundary)
*/
Show that periodic timer intervals stay anchored even if individual deliveries are late.
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
static struct timespec start_ts;
static int count = 0;
void handler(int sig) {
struct timespec now;
long elapsed_ms;
count++;
clock_gettime(CLOCK_MONOTONIC, &now);
elapsed_ms = (now.tv_sec - start_ts.tv_sec) * 1000L
+ (now.tv_nsec - start_ts.tv_nsec) / 1000000L;
printf("Expiry #%d at %ld ms (expected ~%d ms)\n",
count, elapsed_ms, count * 100);
if (count >= 5) {
struct itimerval stop = {{0,0},{0,0}};
setitimer(ITIMER_REAL, &stop, NULL);
}
}
int main(void) {
struct sigaction sa;
struct itimerval itv;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
/* 100ms periodic timer */
itv.it_value.tv_sec = 0;
itv.it_value.tv_usec = 100000;
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 100000;
clock_gettime(CLOCK_MONOTONIC, &start_ts);
setitimer(ITIMER_REAL, &itv, NULL);
while (count < 5)
pause();
printf("Intervals stay anchored — no creep.\n");
return 0;
}
/* Output shows deliveries at ~100ms, ~200ms, ~300ms...
not accumulating error over time */
A jiffy is the duration of one kernel timer tick. It equals 1/HZ seconds. With HZ=250, one jiffy = 4ms. Timers are rounded up to the nearest jiffy boundary.
The timer rounds UP to the next jiffy boundary: 20ms (5 jiffies × 4ms).
Even if one signal delivery is a few ms late, the next expiry is still scheduled at the correct absolute time. The interval is re-loaded from it_interval, anchored to the actual expiry time, not to the late delivery time.
Call clock_getres(CLOCK_REALTIME, &res). If res.tv_nsec == 1, high-resolution timers (CONFIG_HIGH_RES_TIMERS) are active and provide nanosecond precision.
Linux 2.6.21 optionally supports high-resolution timers via CONFIG_HIGH_RES_TIMERS. When enabled, accuracy can reach the hardware limit (~1 microsecond on modern hardware).
