Example code snippet showing the workflow

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:
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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset