You are here: Home / Support / HOWTOs / 
2024-04-23 - 19:42
OSADL HOWTOs

HOWTO: Cyclic execution of a user program function

Cyclic execution of a function in a user program

Carsten Emde

Revision History
Revision 0.12006-12-07
First release
Revision 0.22007-05-30
Changed rtnice to chrt

Introduction

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.

Programming

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

Running

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.

 

Valid XHTML 1.0 Transitional

To top