Real-time Linux (Xenomai)

 Radboud University Nijmegen

Exercise #2: Multi-Tasking


Introduction

Modern real-time systems are based on the complementary concepts of multitasking and intertask communications. A multitasking environment allows real-time applications to be constructed as a set of independent tasks, each with a separate thread of execution and its own set of system resources. The intertask communication facilities allow these tasks to synchronize and coordinate their activities. The Xenomai multitasking scheduler uses interrupt-driven, priority-based preemptive task scheduling. It features fast context switch time and low interrupt latency.


Objectives

The following are the primary objectives of this exercise:

  • To learn how to initiate multiple processes in one-shot or periodic mode with different priorities using Xenomai tasking routines.

Description

Multitasking creates the appearance of many concurrently executing tasks wheras, in fact, the kernel interleaves their execution on the basis of a scheduling algorithm. Each task has its own context, which is the CPU environment and system resources that the task sees each time it is scheduled to run by the kernel. On a context switch, the context of a task is saved in the Task Control Block (TCB). The context of a task includes:

  • a thread of execution, that is, the task's program counter
  • the CPU registers and floating-point registers if necessary
  • a stack of dynamic variables and return addresses of function calls
  • I/O assignments for standard input, output, error
  • a delay timer
  • a timeslice timer
  • kernel control structures
  • signal handlers
  • debugging and performance monitoring values

Time in Xenomai Linux

  • In Xenomai, time is given by the system clock. 
  • When using the native skin, the system clock can be configured with the function rt_timer_set_mode(ns_tick). When ns_tick is 0 the clock is configured to give the time in nanoseconds (which is the default), otherwise the time is given in jiffies where each jiffie is ns_tick nanoseconds long.
  • The system clock in Xenomai is implemented by reading out the Time Stamp Clock (TSC) of the CPU. In case of multiprocessor machines, Xenomai assumes that the TSC of all processors are synchronised. In practice this is a problem with dual core processors, however in newer multiprocessor CPUs the manufacturers have solved this problem. For our exercises, we prevented this problem by building a uniprocessor Xenomai kernel which only uses one processor - even though we might have multiple in hardware.
  • The system clock can be read by the function rt_timer_read(), for instance:
RTIME now; now = rt_timer_read();
  • For RTAI real-time Linux one explicitly has to start the system clock, whereas the system clock in Xenomai is started automatically when the native skin is loaded.
  • Note that the system clock is often called "system timer". But, to avoid confusion with the timer chip, introduced below, we rather use the word "clock" to prevent confusion.

Scheduling in Xenomai Linux

  • In Xenomai Linux, the real-time scheduler is driven by timer interrupts from a special timer chip. This timer chip can be programmed to generate timing interrupts at certain moments.
  • A timer chip itself is driven by a crystal oscillator. This oscillator ticks every baseperiod ns.
  • Hence, the timer chip can only be programmed to generate an interrupt at the resolution of that baseperiod, e.g., to generate an interrupt after  x baseperiods, where x is an integer.
  • For the timer chips in modern PC's the baseperiod is around 1 microsecond, and programming the timer costs around 3 microseconds.
  • Each separate CPU has its own  timer chip which can be programmed either in periodic or one-shot mode independent of all the other CPUs.
  • Xenomai uses this feature to either schedule a task either to run for once at a certain time (use the one-shot mode of timer chip) or to schedule the task repeatingly every period (use the period mode of the timer chip).
  • Periodic scheduling of a task:
    • The period of the task is always a multiple of the timer's baseperiod. Thus taskperiod = X baseperiod  where X is an integer.
    • However for the convenience of the programmer, the Xenomai API lets the programmer specify the time in ns/jiffies (depending on the configuration of the system clock). The API itself recalculates this time into the number of  baseperiods.
  • One-shot scheduling of a task:
    • The timer chip is reprogrammed at the end of each task, such that it expires exactly when the next task is scheduled to run. (This can be another task!)
  • The advantage of periodic mode above one-shot mode, is that the timer only needs to be programmed once in period mode, however in one-shot mode, the timer has to reprogrammed everytime when the task is done, costing around 3 microseconds each time.
  • The disadvantage is that all tasks running on the CPU are forced to run at multiples of the programmed period, because there is only one timer chip per CPU. However for one-shot mode one can schedule a task at anytime in the accuracy of the timer chip which is a baseperiod.
  • It depends on the hardware which timer chip is used, however most PCs nowadays have the following two timer chips :
    • The 8254 Programmable Interval Timer (PIT) chip. (This chip is also used by the normal Linux scheduler.) The resolution is based on frequency of the 8254 chip frequency (1,193,180 Hz), i.e., there is a tick of the 8254 chip every 838 nano seconds, that is around 1 microsecond.
      There is only one 8254 timer for the whole machine. This timer chip is therefore only used for single processor machines.
    • The local APIC timer. The resolution is based on the bus speed, but it is comparable with the 8254 chip.

There is a local APIC timer for every CPU, and is therefore used in multiprocessor systems. Note that older uniprocessor systems did not have a local APIC timer.

  • Some timing considerations : 
    • The timer chip baseperiod  is around 1 microsecond, where as the Time Stamp Counter (TSC) of the CPU is incremented every clock cycle of the CPU, which is around 1 nanosecond for a 1 GHz CPU.  
    • Hence, the TSC frequency is  much higher than that of the timer chip (1000 times for a 1 GHz CPU). 
  • Remember that the timer chip is meant to generate interrupts. The TSC cannot generate interrupts, but can only be used as a clock!

Note: In the latest Xenomai versions one can have a different periodic mode per skin, and have programs using different skins to run at the same time. To implement this, one does not use the periodic mode of the timer chip anymore. Instead all timings are done using one-shot programming of the timer chip. Periodic mode is emulated by a software driver which implements it by doing one-shot programming of the system timer periodically.

Scheduling the Task

one-shot mode

  • One-shote mode is the default when a task is started by the 'rt_task_start' function introduced in exercise 1.

periodic mode

  • A periodic task is also started by the 'rt_task_start' function, but it made periodic executing the command:
int rt_task_set_periodic(RT_TASK *task,
                         RTIME start_time,
                         RTIME period);
    • 'task' is the descriptor of the affected task. This task is immediately delayed until the first periodic release point is reached. If task is NULL, the current task is set periodic.
    • 'start_time' is the absolute time, expressed in clock ticks ( nanoseconds or jiffies), when the task should begin execution. If 'start_time' is equal to TM_NOW, the current system date is used, and no initial delay takes place.
    • 'period' is the task's period, in clock ticks, which will be rounded to the nearest multiple of the period of the system timer in periodic mode.
      Note: for newer Xenomai  periodic mode is emulated by a software driver (see the note above) which uses one-shot mode programming of the timer chip instead. This leads to an accurracy of the task's period in the order of the baseperiod of the timer chip.
  • When a period task has perfomed its functionality, it releases the processor and starts waiting for the next period with the command:
void rt_task_wait_period(NULL);

As an example, consider a task function which executes an infinite loop which typically reads some inputs, computes some outputs and waits for the next period. Typical code looks like this:

void task_function(int *arg)
{
  rt_task_set_periodic(NULL, TM_NOW, period_ns); 
  while (1) {
    /*
      Do your thing here
     */
    rt_task_wait_period(NULL);
  }
  return;
}

Exercises 

Exercise 2a.  

Write a program which runs five tasks with names with the same priority in one-shot mode. Each task executes the following function  :



void demo(void *arg) {
    RT_TASK *curtask;
    RT_TASK_INFO curtaskinfo;
    curtask=rt_task_self();
    rt_task_inquire(curtask,&curtaskinfo);
    rt_printf("Task name: %s ", curtaskinfo.name);
}


Execute the program and describe the resulting output.

Exercise 2b. 

An integer argument can be passed to a task during the call to rt_task_start(). Pass a unique argument to each of the five tasks and let them print this number. Cast and derefence the number using, for instance,

int num = * (int *)arg;

Again, each task has the same priority. Execute the program and describe its output.

Exercise 2c. 

Modify the program of exercise 2b by assigning each task a unique priority such that the task which is started first gets the lowest priority and tasks started later get higher priority. Is there any difference in output? Has the order in which the five tasks print their message changed? If not, explain why.

Exercise 2d. 

Rewrite the program of exercise 2b into one where three tasks are running periodically with periods of 1, 2 and 3 seconds, respectively. All tasks have the same priority; they should start with a sleep of 1 second and next execute a loop in which they print a message. To avoid termination when the main loop stops, end the main loop with the statements:

    rt_printf("end program by CTRL-C\n");
    pause();

Explain the resulting output.


Last Updated: 29 August 2009 (by Jozef Hooman)
Created by: Harco Kuppens
h.kuppens@cs.ru.nl