Timers/Process
Signal or Thread
Detection
Why POSIX Timers Over setitimer()?
Classical setitimer() has severe limitations: only one timer per type, only signal delivery, no overrun count, microsecond limit.
POSIX.1b timers (Linux 2.6+) fix all of these: multiple timers, choice of notification method, overrun detection, and nanosecond resolution.
| Feature | setitimer() | POSIX timer_*() |
|---|---|---|
| Number of timers | 1 per type (3 total) | Many (limited by queued RT signals) |
| Resolution | Microseconds | Nanoseconds |
| Notification method | Signal only | Signal, thread function, or none |
| Overrun detection | No | Yes — timer_getoverrun() |
| Clock selection | REAL, VIRTUAL, PROF only | Any POSIX clock |
| fork() inheritance | Not inherited | Not inherited, deleted on exec() |
timer_create()
Create & define
notification
timer_settime()
Arm (start)
or disarm
Notify
Signal or
Thread
timer_gettime()
Query
remaining
timer_delete()
Free
resources
#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <time.h>
/* Create a new timer */
int timer_create(clockid_t clockid,
struct sigevent *evp,
timer_t *timerid);
/* Arm or disarm a timer */
int timer_settime(timer_t timerid,
int flags, /* 0 or TIMER_ABSTIME */
const struct itimerspec *value,
struct itimerspec *old_value); /* NULL if unused */
/* Get current timer state */
int timer_gettime(timer_t timerid,
struct itimerspec *curr_value);
/* Delete a timer */
int timer_delete(timer_t timerid);
/* Get overrun count */
int timer_getoverrun(timer_t timerid);
/* All return 0 on success, -1 on error */
/* itimerspec uses nanosecond precision: */
struct itimerspec {
struct timespec it_interval; /* Repeat interval */
struct timespec it_value; /* First expiry */
};
/* Compile: gcc prog.c -o prog -lrt */
| sigev_notify value | Action on expiry | Standard? |
|---|---|---|
| SIGEV_NONE | No notification — poll manually via timer_gettime() | ✅ SUSv3 |
| SIGEV_SIGNAL | Send signal sigev_signo to process | ✅ SUSv3 |
| SIGEV_THREAD | Call sigev_notify_function in a new thread | ✅ SUSv3 |
| SIGEV_THREAD_ID | Send signal to specific thread (Linux only) | ❌ Linux-specific |
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define TIMER_SIG SIGRTMIN /* Use first realtime signal */
static timer_t timerid;
static volatile int expiry_count = 0;
void timer_handler(int sig, siginfo_t *si, void *uc) {
(void)sig; (void)uc;
expiry_count++;
/* si->si_value.sival_ptr points to our timerid */
printf("Timer fired! expiry #%d, overrun=%d\n",
expiry_count,
timer_getoverrun(*(timer_t *)si->si_value.sival_ptr));
}
int main(void) {
struct sigaction sa;
struct sigevent sev;
struct itimerspec ts;
/* Install signal handler (SA_SIGINFO to get siginfo_t) */
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
sigaction(TIMER_SIG, &sa, NULL);
/* Configure: signal on expiry */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = TIMER_SIG;
sev.sigev_value.sival_ptr = &timerid; /* Pass timer ID to handler */
/* Create timer using CLOCK_REALTIME */
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create"); return 1;
}
printf("Timer ID: %ld\n", (long)timerid);
/* Arm: first expiry after 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 (timer_settime(timerid, 0, &ts, NULL) == -1) {
perror("timer_settime"); return 1;
}
/* Wait for 5 expirations */
while (expiry_count < 5)
pause();
/* Clean up */
timer_delete(timerid);
printf("Done.\n");
return 0;
}
/* Compile: gcc -o posix_timer posix_timer.c -lrt
Output:
Timer ID: 140366...
Timer fired! expiry #1, overrun=0
Timer fired! expiry #2, overrun=0
Timer fired! expiry #3, overrun=0
Timer fired! expiry #4, overrun=0
Timer fired! expiry #5, overrun=0
Done.
*/
Create two POSIX timers with different intervals running at the same time.
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#define SIG_T1 SIGRTMIN
#define SIG_T2 (SIGRTMIN + 1)
void handler_t1(int sig, siginfo_t *si, void *uc) {
(void)sig; (void)si; (void)uc;
printf("Timer 1 (500ms) fired\n");
}
void handler_t2(int sig, siginfo_t *si, void *uc) {
(void)sig; (void)si; (void)uc;
printf(" Timer 2 (1500ms) fired\n");
}
timer_t create_timer(int signo, void (*handler)(int,siginfo_t*,void*),
long first_ms, long interval_ms) {
struct sigaction sa;
struct sigevent sev;
struct itimerspec ts;
timer_t tid;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sigaction(signo, &sa, NULL);
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = signo;
sev.sigev_value.sival_int = signo;
timer_create(CLOCK_REALTIME, &sev, &tid);
ts.it_value.tv_sec = first_ms / 1000;
ts.it_value.tv_nsec = (first_ms % 1000) * 1000000L;
ts.it_interval.tv_sec = interval_ms / 1000;
ts.it_interval.tv_nsec = (interval_ms % 1000) * 1000000L;
timer_settime(tid, 0, &ts, NULL);
return tid;
}
int main(void) {
timer_t t1, t2;
printf("Starting two timers...\n");
/* Timer 1: every 500ms */
t1 = create_timer(SIG_T1, handler_t1, 500, 500);
/* Timer 2: every 1500ms */
t2 = create_timer(SIG_T2, handler_t2, 1500, 1500);
/* Run for ~4 seconds */
sleep(4);
timer_delete(t1);
timer_delete(t2);
printf("Both timers deleted.\n");
return 0;
}
/* Expected output:
Timer 1 (500ms) fired
Timer 1 (500ms) fired
Timer 1 (500ms) fired <-- T=1500ms
Timer 2 (1500ms) fired
Timer 1 (500ms) fired
... interleaved based on timing
*/
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
int main(void) {
timer_t timerid;
struct sigevent sev;
struct itimerspec ts, curr;
/* SIGEV_NONE: no notification — just poll with timer_gettime() */
sev.sigev_notify = SIGEV_NONE;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create"); return 1;
}
/* Arm for 10 seconds */
ts.it_value.tv_sec = 10;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
timer_settime(timerid, 0, &ts, NULL);
/* Poll remaining time every second */
for (int i = 0; i < 3; i++) {
sleep(1);
timer_gettime(timerid, &curr);
printf("Remaining: %ld.%09ld s\n",
curr.it_value.tv_sec,
curr.it_value.tv_nsec);
}
/* Disarm by setting it_value to 0 */
memset(&ts, 0, sizeof(ts));
timer_settime(timerid, 0, &ts, NULL);
timer_gettime(timerid, &curr);
printf("After disarm — remaining: %ld s\n",
curr.it_value.tv_sec);
timer_delete(timerid);
return 0;
}
/* Output:
Remaining: 8.999... s
Remaining: 7.999... s
Remaining: 6.999... s
After disarm — remaining: 0 s
*/
Create (timer_create), Arm (timer_settime with nonzero it_value), Receive notification, Delete (timer_delete). Optionally query via timer_gettime.
Call timer_settime() with both subfields of it_value set to 0.
They are disarmed and deleted. Unlike setitimer() which persists across exec().
It defaults to SIGEV_SIGNAL with signal SIGALRM and sival_int set to the timer ID — same as the old alarm() behavior.
SA_SIGINFO enables the siginfo_t argument in the handler, which gives access to si_code (SI_TIMER), si_value (your custom data passed at timer_create time), and si_overrun (Linux extension). Without it, you lose all this context.
Part 8: Overruns & Thread Notify ← Part 6: clock_nanosleep()
