timerfd API in Linux: Complete Developer Guide
timerfd API in Linux: Complete Developer Guide
Kernel ≥ 2.6.25
select/poll/epoll
uint64_t count
What Is the timerfd API?
The timerfd API (Linux 2.6.25+) creates timers whose expirations can be read from a file descriptor. This is a game-changer for event-driven programs — you can include timers in select(), poll(), or epoll alongside sockets, pipes, and other fds.
No signal handling required. No race conditions. Works perfectly in multi-threaded programs.
Use timerfd when…
- Building event loops (epoll-based servers)
- Mixing timers with I/O in select()/poll()
- Multi-threaded programs (no signal safety concerns)
- Want clean, readable expiration count
Use POSIX timers when…
- Portability across POSIX systems
- Signal-based notification preferred
- Thread start function on expiry (SIGEV_THREAD)
- Profiling timers (CPUTIME clocks)
timerfd_create()
Returns fd
timerfd_settime()
Arm timer
Timer fires
fd becomes
readable
read(fd, &n, 8)
n = # expirations
close(fd)
when done
#include <sys/timerfd.h>
/* Create a timer file descriptor */
int timerfd_create(int clockid, /* CLOCK_REALTIME or CLOCK_MONOTONIC */
int flags); /* 0, TFD_CLOEXEC, TFD_NONBLOCK */
/* Returns: fd on success, -1 on error */
/* Arm or disarm the timer */
int timerfd_settime(int fd,
int flags, /* 0 or TFD_TIMER_ABSTIME */
const struct itimerspec *new_value,
struct itimerspec *old_value); /* NULL if unused */
/* Returns: 0 on success, -1 on error */
/* Query current timer state */
int timerfd_gettime(int fd,
struct itimerspec *curr_value);
/* Returns: 0 on success, -1 on error */
/* Read expiration count (BLOCKING unless TFD_NONBLOCK) */
#include <stdint.h>
ssize_t read(int fd, uint64_t *buf, 8);
/* buf = number of expirations since last read / settime
Blocks if no expirations yet.
Returns 8 on success, -1 on error */
/* When done: */
close(fd);
/* Compile: no extra -l flags needed (built into kernel) */
| Flag | Used In | Effect |
|---|---|---|
| TFD_CLOEXEC | timerfd_create() | Set close-on-exec flag on fd (like O_CLOEXEC) |
| TFD_NONBLOCK | timerfd_create() | Non-blocking reads: return EAGAIN if no expirations |
| TFD_TIMER_ABSTIME | timerfd_settime() | new_value is absolute time (like TIMER_ABSTIME) |
fork()
Child inherits COPIES of timerfd file descriptors. Both parent and child can read timer expirations from the SAME timer object.
exec()
timerfd fds are preserved across exec() (unless TFD_CLOEXEC was set). Armed timers continue generating expirations after exec.
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <time.h>
int main(void) {
int fd;
struct itimerspec ts;
uint64_t expirations;
ssize_t n;
/* Create timer fd using monotonic clock */
fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (fd == -1) { perror("timerfd_create"); return 1; }
/* Arm: first expiry in 1s, repeat every 1s */
ts.it_value.tv_sec = 1;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
if (timerfd_settime(fd, 0, &ts, NULL) == -1) {
perror("timerfd_settime"); return 1;
}
printf("Timer started. Reading 5 expirations...\n");
for (int i = 0; i < 5; i++) {
/* read() blocks until timer expires */
n = read(fd, &expirations, sizeof(uint64_t));
if (n != sizeof(uint64_t)) {
perror("read"); return 1;
}
printf("Expiration #%d: count=%llu\n",
i + 1, (unsigned long long)expirations);
}
close(fd);
printf("Done.\n");
return 0;
}
/* Compile: gcc -o tfd_basic tfd_basic.c
Output:
Timer started. Reading 5 expirations...
Expiration #1: count=1
Expiration #2: count=1
Expiration #3: count=1
Expiration #4: count=1
Expiration #5: count=1
Done.
*/
The key feature: include timer fd in select() alongside stdin.
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/select.h>
#include <time.h>
#include <string.h>
int main(void) {
int tfd;
struct itimerspec ts;
uint64_t expirations;
char buf[128];
fd_set read_fds;
int nfds;
/* Create 2-second periodic timer */
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
ts.it_value.tv_sec = 2;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 2;
ts.it_interval.tv_nsec = 0;
timerfd_settime(tfd, 0, &ts, NULL);
nfds = (tfd > STDIN_FILENO ? tfd : STDIN_FILENO) + 1;
printf("Watching stdin AND timer (every 2s). Type or wait...\n");
printf("Press Ctrl-C to exit.\n");
while (1) {
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO, &read_fds); /* Watch keyboard */
FD_SET(tfd, &read_fds); /* Watch timer */
if (select(nfds, &read_fds, NULL, NULL, NULL) == -1) {
perror("select"); break;
}
if (FD_ISSET(tfd, &read_fds)) {
read(tfd, &expirations, sizeof(uint64_t));
printf("[TIMER] %llu expiration(s) occurred\n",
(unsigned long long)expirations);
}
if (FD_ISSET(STDIN_FILENO, &read_fds)) {
ssize_t n = read(STDIN_FILENO, buf, sizeof(buf) - 1);
if (n > 0) {
buf[n] = '\0';
printf("[INPUT] You typed: %s", buf);
}
}
}
close(tfd);
return 0;
}
/* This program handles both user input and timer ticks
using a single select() — no signals needed at all! */
Production-grade: integrate timer into an epoll event loop.
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <time.h>
#define MAX_EVENTS 4
#define TICK_MS 500 /* 500ms tick */
int main(void) {
int epfd, tfd;
struct itimerspec ts;
struct epoll_event ev, events[MAX_EVENTS];
uint64_t expirations;
int n, ticks = 0;
/* Create epoll instance */
epfd = epoll_create1(0);
/* Create timerfd */
tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
/* Arm timer: 500ms periodic */
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = TICK_MS * 1000000L;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = TICK_MS * 1000000L;
timerfd_settime(tfd, 0, &ts, NULL);
/* Register timerfd with epoll */
ev.events = EPOLLIN;
ev.data.fd = tfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, tfd, &ev);
printf("Event loop running (500ms ticks)...\n");
while (ticks < 6) {
n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == tfd) {
read(tfd, &expirations, sizeof(uint64_t));
ticks += (int)expirations;
printf("Tick! expirations=%llu, total_ticks=%d\n",
(unsigned long long)expirations, ticks);
/* In a real server: ticks could trigger
timeouts, keepalives, housekeeping... */
}
/* Add more fds (sockets, pipes) to this loop */
}
}
close(tfd);
close(epfd);
printf("Event loop done.\n");
return 0;
}
/* This is the pattern used by:
- libuv (Node.js event loop)
- libev, libevent
- many high-performance servers
*/
Q1. What is the main advantage of timerfd over POSIX timers?
Timer expirations can be read via a file descriptor, so they integrate naturally with select(), poll(), and epoll event loops. No signal handlers needed, no async-signal restrictions.
Q2. What does read() return from a timerfd?
An 8-byte uint64_t containing the number of timer expirations since the last read() or settime(). If multiple expirations happened (overruns), you get them all in one read.
Q3. What clock IDs can timerfd_create() use?
CLOCK_REALTIME or CLOCK_MONOTONIC. Unlike POSIX timers, CPU-time clocks (CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID) are not supported.
Q4. What happens to timerfd fds after fork()?
Both parent and child inherit copies of the fd referring to the SAME timer object. Either can read expirations — but each expiration is consumed by only one read() call.
Q5. What is TFD_NONBLOCK and when is it useful?
Makes read() non-blocking — returns EAGAIN instead of blocking if no expirations. Useful in event loops where you want to poll the timer fd without blocking the event thread.
| API | When to Use | Precision | Header |
|---|---|---|---|
alarm() |
Simple one-shot real timer | Seconds | <unistd.h> |
setitimer() |
Classic interval timers (3 types) | Microseconds | <sys/time.h> |
sleep() |
Simple process suspend | Seconds | <unistd.h> |
nanosleep() |
High-precision sleep | Nanoseconds | <time.h> |
clock_gettime() |
Read various POSIX clocks | Nanoseconds | <time.h> -lrt |
clock_nanosleep() |
Drift-free precise sleeping | Nanoseconds | <time.h> -lrt |
timer_create() |
Multiple POSIX timers, signal/thread notify | Nanoseconds | <time.h> -lrt |
timerfd_create() |
Event-loop timers, epoll/select integration | Nanoseconds | <sys/timerfd.h> |
You have covered all 9 parts of Linux Timers & Sleeping.
