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.
- 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
- 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) |
Child inherits COPIES of timerfd file descriptors. Both parent and child can read timer expirations from the SAME timer object.
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
*/
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.
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.
CLOCK_REALTIME or CLOCK_MONOTONIC. Unlike POSIX timers, CPU-time clocks (CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID) are not supported.
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.
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.
