In the following, we display code snippets from our sample program ch13/react.c (see more on this rather interesting reaction time game app in the following section), which clearly illustrates the sequence of steps previously described.
- Signal(s) set up:
- Assuming the notification mechanism being used is a signal, first trap the signal(s) via sigaction(2) as follows:
struct sigaction act;
[...]
// Trap SIGRTMIN : delivered on (interval) timer expiry
memset(&act, 0, sizeof(act));
act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = timer_handler;
if (sigaction(SIGRTMIN, &act, NULL) == -1)
FATAL("sigaction SIGRTMIN failed ");
- Create and initialize the timer(s):
- Decide on the clock type (or source) to use to measure the elapsed time:
- We use the real-time clock CLOCK_REALTIME the system-wide wall clock time, as our timer source.
- Decide on the timer-expiry event-notification mechanism to be used by your application—typically, whether to use (the usual) signals or a (newly spawned) thread.
- We use signaling as the timer-expiry event-notification mechanism.
- The aforementioned decisions are implemented via the timer_create(2) system call, which allows one to create a timer; of course, we can create multiple timers by invoking it multiple times:
- Decide on the clock type (or source) to use to measure the elapsed time:
struct sigevent sev;
[...]
/* Create and init the timer */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1)
FATAL("timer_create failed ");
-
Arm (or disarm) a particular timer using the timer_settime(2) API. To arm a timer means to effectively start it running, or counting down; disarming a timer is the opposite—stopping it in its tracks:
static struct itimerspec itv; // global
[...]
static void arm_timer(timer_t tmrid, struct itimerspec *itmspec)
{
VPRINT("Arming timer now ");
if (timer_settime(tmrid, 0, itmspec, NULL) == -1)
FATAL("timer_settime failed ");
jumped_the_gun = 0;
}
[...]
printf("Initializing timer to generate SIGRTMIN every %ld ms ",
freq_ms);
memset(&itv, 0, sizeof(struct itimerspec));
itv.it_value.tv_sec = (freq_ms * 1000000) / 1000000000;
itv.it_value.tv_nsec = (freq_ms * 1000000) % 1000000000;
itv.it_interval.tv_sec = (freq_ms * 1000000) / 1000000000;
itv.it_interval.tv_nsec = (freq_ms * 1000000) % 1000000000;
[...]
arm_timer(timerid, &itv);
-
To query the time remaining (to expiration) in a particular timer (and its interval setting), use timer_gettime(2)
This is not performed in this particular application.
-
Check the overrun count of a given timer using timer_getoverrun(2)
An explanation of what this API does, and why we might need it, is provided in the following section, Figuring the overrun.
/*
* The realtime signal (SIGRTMIN) - timer expiry - handler.
* WARNING! Using the printf in a signal handler is unsafe!
* We do so for the purposes of this demo app only; do Not
* use in production.
*/
static void timer_handler(int sig, siginfo_t * si, void *uc)
{
char buf[] = ".";
c++;
if (verbose) {
write(2, buf, 1);
#define SHOW_OVERRUN 1
#if (SHOW_OVERRUN == 1)
{
int ovrun = timer_getoverrun(timerid);
if (ovrun == -1)
WARN("timer_getoverrun");
else {
if (ovrun)
printf(" overrun=%d [@count=%d] ", ovrun, c);
}
}
#endif
}
}
- Delete (and obviously disarm) a timer using timer_delete(2)
This is not performed in this particular application (as the process exit will, of course, delete all timers associated with the process.)
As the man page on timer_create(2) informs us, a few more points to note on POSIX (interval) timers are as follows:
- Upon fork(2), all timers get auto-disarmed; in other words, timers are not going to continue towards expiry in the child process.
- Upon execve(2), all timers are deleted and will thus not be visible in the successor process.
- Something useful of note is that (from the Linux 3.10 kernel onward) the proc filesystem can be used to query the timer(s) a process owns; just lookup cat the pseudo-file /proc/<pid>/timers to see them (if they exist).
- From the Linux 4.10 kernel onward, POSIX timers are a kernel-configurable option (at kernel build time, they are enabled by default).
As we have repeatedly mentioned, the man pages are a very precious and useful resource that is available to developers; again, the man page on timer_create(2) (https://linux.die.net/man/2/timer_create) provides a nice example program; we urge the reader to refer to the man page, read it, build it and try the program out.