Revision History | |
---|---|
Revision 0.1 | 2006-12-07 |
First release | |
Revision 0.2 | 2007-05-30 |
Changed rtnice to chrt |
Table of Contents
The rationale behind the realtime-preempt kernel patch is to equip the Linux kernel with realtime capabilities without creating an additional programming API. Instead, the entire management of timing and other functions that interfere with timed execution control is realized through the existing POSIX API.
This HOWTO presents a small programming example how to install a signal handler and to schedule cyclic alarms. Again, this example is in no aspect any special or typical for a realtime system - it will compile and run on any system. The only difference between a realtime and a non-realtime system is that only a realtime system will guarantee that every alarm signal will be sent to the program at the right time and no one is lost - if the timing is within the specified range of a particular system.
The following program listing exemplifies the usage of POSIX timer calls from a program running in user space.
#include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <time.h> #include <sys/time.h> #define INTERVAL 200 /* microseconds */ #define DURATION 2 /* seconds */ void signalhandler(int signo) { /* Do something every INTERVAL microseconds */ /* For example, write a capital A to stdout */ char c = 'A'; write (1, &c, 1); } int main(int argc, char *argv[]) { struct sigaction action; struct itimerval timer; struct timespec ts, rem; struct timeval end, now; /* Catch SIGALRM */ action.sa_handler = signalhandler; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigaction (SIGALRM, &action, NULL); /* Send SIGALRM every INTERVAL microseconds */ timer.it_value.tv_sec = timer.it_interval.tv_sec = 0; timer.it_value.tv_usec = timer.it_interval.tv_usec = INTERVAL; setitimer(ITIMER_REAL, &timer, NULL); /* Define sleep parameters */ ts.tv_sec = DURATION; ts.tv_nsec = 0; rem = ts; /* Calculate end time of program run */ gettimeofday(&end, NULL); end.tv_sec += DURATION; while (rem.tv_sec || rem.tv_nsec) { /* Continue sleep, if woken up by our own signal */ if (nanosleep(&ts, &rem) == -1 && errno == EINTR) ts = rem; else break; /* DURATION elapsed? */ gettimeofday(&now, NULL); if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) break; } return(0); }
Since we only have a single source file, we can use the implicit Makefile:
# make user-cyclic cc user-cyclic.c -o user-cyclic
The above example program will write one character every 200 microseconds during 2 seconds to the standard output path. If everything works as expected, a total of 10,000 characters must be written. Let's see:
# ./user-cyclic | wc -m 10000
And now, how does our program behave under heavy load?
# hackbench 20 & ./user-cyclic | wc -m 8921
Oops, why is that?
Our program was running at the default priority - which is not enough to successfully compete against hackbench. So we need to run our program at a higher priority:
# hackbench 20 & chrt 1 ./user-cyclic | wc -m 10000
The program chrt is included in many standard Linux distributions as part of the util-linux package.