Fundamentals 16 min read

Master Linux Timers: From alarm() to timerfd – Choose the Right One

This article walks through the evolution of Linux timers—from the simple alarm() function, through setitimer() and POSIX timer APIs, to the modern timerfd interface—explaining their APIs, code examples, advantages, limitations, performance trade‑offs, and practical selection guidance for different projects.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Master Linux Timers: From alarm() to timerfd – Choose the Right One

First generation: classic alarm()

The original Linux timer is the alarm() function, which schedules a SIGALRM after a given number of seconds.

#include <unistd.h>
#include <signal.h>
#include <stdio.h>

void timeout_handler(int sig) {
    printf("Time's up!
");
}

int main() {
    signal(SIGALRM, timeout_handler);
    alarm(5); // trigger after 5 seconds
    pause(); // wait for signal
    return 0;
}

Drawbacks: only second granularity, only one alarm per process (later calls overwrite earlier ones), and it interferes with system calls such as sleep() and read() that can be interrupted by the signal.

Typical pitfall

Using several alarm() calls in a multi‑module project causes timers to overwrite each other, leading to unexpected behavior.

Second generation: setitimer()

setitimer()

adds microsecond precision and periodic triggering. It supports three timer types: ITIMER_REAL, ITIMER_VIRTUAL, and ITIMER_PROF.

#include <sys/time.h>
#include <signal.h>
#include <stdio.h>

void timer_handler(int sig) {
    static int count = 0;
    printf("Timer fired %d times!
", ++count);
}

int main() {
    struct itimerval timer;
    signal(SIGALRM, timer_handler);
    // first trigger after 1 second, then every 0.5 seconds
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 500000; // 500 ms
    setitimer(ITIMER_REAL, &timer, NULL);
    while (1) pause();
    return 0;
}

Advantages: microsecond precision, periodic mode, three timer categories. New issues: still signal‑based (with associated pitfalls), only one ITIMER_REAL per process, and signals can be lost while a handler runs.

Third generation: POSIX timers

POSIX timers created with timer_create() provide nanosecond precision and allow multiple independent timers. Notification can be a signal, a thread, or none (polling).

#include <time.h>
#include <signal.h>
#include <stdio.h>

timer_t timerid;
int timer_count = 0;

void timer_handler(int sig, siginfo_t *si, void *uc) {
    timer_t *tidp = si->si_value.sival_ptr;
    printf("POSIX timer %p fired %d times!
", tidp, ++timer_count);
}

int main() {
    struct sigevent sev;
    struct itimerspec its;
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timer_handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGUSR1, &sa, NULL);
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGUSR1;
    sev.sigev_value.sival_ptr = &timerid;
    if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
        perror("timer_create failed");
        return -1;
    }
    its.it_value.tv_sec = 1; its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 500000000; // 500 ms
    timer_settime(timerid, 0, &its, NULL);
    printf("POSIX timer started, press Ctrl+C to exit
");
    while (1) pause();
    timer_delete(timerid);
    return 0;
}

Benefits: multiple timers, nanosecond precision, flexible notification, ability to pass extra data via siginfo_t. Drawbacks: still relies on signals, more complex code, and portability varies (full support on recent Linux, limited on macOS/BSD, may require linking with -lrt on older glibc).

Fourth generation: timerfd

Since Linux 2.6.25, timerfd turns a timer into a file descriptor, enabling integration with select(), poll(), or epoll().

#include <sys/timerfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

int main() {
    int timer_fd = timerfd_create(CLOCK_REALTIME, 0);
    if (timer_fd == -1) { perror("timerfd_create"); return -1; }
    struct itimerspec timer_spec;
    timer_spec.it_value.tv_sec = 2; timer_spec.it_value.tv_nsec = 0; // first trigger after 2 s
    timer_spec.it_interval.tv_sec = 1; timer_spec.it_interval.tv_nsec = 0; // then every 1 s
    timerfd_settime(timer_fd, 0, &timer_spec, NULL);
    printf("Timer started, waiting for expirations...
");
    for (int i = 0; i < 5; ++i) {
        uint64_t expirations;
        ssize_t s = read(timer_fd, &expirations, sizeof(expirations));
        if (s == sizeof(expirations))
            printf("Timer fired %llu times
", expirations);
    }
    close(timer_fd);
    return 0;
}

Key advantages: represented as a file descriptor, nanosecond precision, unlimited number of timers (limited only by file‑descriptor limits), no signal handling, and seamless use in event‑driven programs.

Example combining two timerfd objects with epoll for high‑performance monitoring:

#include <stdio.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <stdint.h>

int main() {
    int timerfd1 = timerfd_create(CLOCK_REALTIME, 0);
    int timerfd2 = timerfd_create(CLOCK_REALTIME, 0);
    int epollfd = epoll_create1(0);
    struct epoll_event ev, events[10];
    ev.events = EPOLLIN;
    ev.data.fd = timerfd1; epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd1, &ev);
    ev.data.fd = timerfd2; epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd2, &ev);
    struct itimerspec its;
    // timerfd1: 1‑second interval
    its.it_value.tv_sec = 1; its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 1; its.it_interval.tv_nsec = 0;
    timerfd_settime(timerfd1, 0, &its, NULL);
    // timerfd2: 2‑second interval
    its.it_value.tv_sec = 2; its.it_interval.tv_sec = 2;
    timerfd_settime(timerfd2, 0, &its, NULL);
    printf("High‑performance timer system started!
");
    while (1) {
        int nfds = epoll_wait(epollfd, events, 10, -1);
        for (int n = 0; n < nfds; ++n) {
            uint64_t exp;
            read(events[n].data.fd, &exp, sizeof(exp));
            if (events[n].data.fd == timerfd1)
                printf("⚡ Fast timer (1 s) triggered
");
            else if (events[n].data.fd == timerfd2)
                printf("🐌 Slow timer (2 s) triggered
");
        }
    }
    return 0;
}

Practical decision guide

Simple one‑off delay : use alarm() – minimal code, sufficient for tiny scripts.

Periodic timer with modest precision : setitimer() – works on all Unix‑like systems.

Multiple timers, nanosecond precision, portable POSIX : timer_create() / timer_settime().

Modern Linux server or high‑concurrency network program : timerfd_create() combined with epoll() – best performance and clean integration.

Performance comparison

alarm()

: only one global timer; new calls overwrite old ones. setitimer(): one per type (REAL, VIRTUAL, PROF) – up to three concurrent timers.

POSIX timers: multiple instances (hundreds typical), but signal handling overhead. timerfd(): many instances limited only by file‑descriptor limits; no signals.

Compatibility notes

alarm()

/ setitimer(): universally available on Unix.

POSIX timers: standardized but support varies; Linux may need -lrt on older glibc. timerfd(): Linux‑only (kernel 2.6.25+).

Advanced tips

High‑precision timers: use CLOCK_MONOTONIC with timerfd_create() to avoid jumps caused by system‑time changes.

One‑shot timers: set it_interval fields to zero.

Timer manager pattern (simplified):

typedef struct {
    int fd;
    void (*callback)(void *data);
    void *data;
} Timer;

Timer* create_timer(int interval_ms, void (*cb)(void*), void *data);
void destroy_timer(Timer *t);
void handle_timer_event(Timer *t);

Encapsulating timer creation and callback handling keeps large projects tidy.

Conclusion

The evolution from alarm() to timerfd() mirrors broader trends in system programming: moving from simple, global, signal‑driven APIs toward fine‑grained, file‑descriptor‑based, event‑driven designs that scale in high‑performance, concurrent applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ClinuxTimersPOSIXalarmtimerfdsetitimer
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.