· 5 min read · 10 comments
Robotics Software

Real-Time Programming with Xenomai 3 - Part 2: Writing a simple periodic task.

I walk you through writing a simple cyclic task in Xenomai.

Xenomai gets tasks to run in real-time by having a co-kernel running alongside the regular linux kernel handling all the time critical tasks. The Xenomai co-kernel is able to do this because of the i-pipe patch that the custom kernel is compiled with. This patch adds an interrupt pipeline that sits between the hardware of the computer and any kernels running on the hardware. The interrupt pipeline has domains which can be assigned a priority. When any interrupt, system call or processor fault comes in, the domain with the higher priority is allowed to process them first. The Xenomai co-kernel has the higher priority in an ipipe patched kernel. The Xenomai website has a more detailed explanation of how it works.

Before I start with the explanation, here’s the full code and Makefile for those who just want to compile some code and get started. The documentation for all the functions used in the code and more can be found here.

The code:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <math.h>


#define CLOCK_RES 1e-9 //Clock resolution is 1 ns by default
#define LOOP_PERIOD 1e7 //Expressed in ticks
//RTIME period = 1000000000;
RT_TASK loop_task;

void loop_task_proc(void *arg)
{
  RT_TASK *curtask;
  RT_TASK_INFO curtaskinfo;
  int iret = 0;

  RTIME tstart, now;

  curtask = rt_task_self();
  rt_task_inquire(curtask, &curtaskinfo);
  int ctr = 0;

  //Print the info
  printf("Starting task %s with period of 10 ms ....\n", curtaskinfo.name);

  //Make the task periodic with a specified loop period
  rt_task_set_periodic(NULL, TM_NOW, LOOP_PERIOD);

  tstart = rt_timer_read();

  //Start the task loop
  while(1){
    printf("Loop count: %d, Loop time: %.5f ms\n", ctr, (rt_timer_read() - tstart)/1000000.0);
    ctr++;
    rt_task_wait_period(NULL);
  }
}

int main(int argc, char **argv)
{
  char str[20];

  //Lock the memory to avoid memory swapping for this program
  mlockall(MCL_CURRENT | MCL_FUTURE);
    
  printf("Starting cyclic task...\n");

  //Create the real time task
  sprintf(str, "cyclic_task");
  rt_task_create(&loop_task, str, 0, 50, 0);

  //Since task starts in suspended mode, start task
  rt_task_start(&loop_task, &loop_task_proc, 0);

  //Wait for Ctrl-C
  pause();

  return 0;
}

The Makefile

SKIN=alchemy
MAIN_SRC=cyclic_test
TARGET=cyclic_test

LM=-lm

CFLAGS := $(shell xeno-config --skin=alchemy --cflags)
LDFLAGS := $(LM) $(shell xeno-config --skin=alchemy --ldflags)
CC := $(shell xeno-config --cc)

$(TARGET): $(MAIN_SRC).c
	$(CC) -o $@ $< $(CFLAGS) $(LDFLAGS)

First, the headers, defines and global variables:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <math.h>


#define CLOCK_RES 1e-9 //Clock resolution is 1 ns by default
#define LOOP_PERIOD 1e7 //Expressed in ticks
//RTIME period = 1000000000;
RT_TASK loop_task;

The includes are fairly standard. The Xenomai libraries are included by the alchemy/task.h and the alchemy/timer.h statements. I’ve deifined CLOCK_RES (The resolution of the clock) and LOOP_PERIOD (The period with which I want the periodic task to run) for convenience. The variable RT_TASK loop_task will hold an address to a task descriptor for a Real-Time task/thread that Xenomai will create.

Jumping ahead to main(), the first line that you come across that might be unfamiliar is:

  //Lock the memory to avoid memory swapping for this program
  mlockall(MCL_CURRENT | MCL_FUTURE);

The mlockall() function is actually a function provided by linux rather than Xenomai and is provided by the <sys/mman.h> include. In Part one, I talked about how a real-time task can miss its deadlines if the task is swapped out of memory by the operating system. This line of code makes sure that the memory that is currently mapped to the address space of the process as well as any memory that gets mapped into the address space of the process in the future is “locked” into RAM and cannot get swapped out.

In the next few lines of code, a new real-time task is created.

  //Create the real time task
  sprintf(str, "cyclic_task");
  rt_task_create(&loop_task, str, 0, 50, 0);

The rt_task_create() function creates a new real-time task using Xenomai’s Alchemy API. The first argument is the RT_TASK variable that holds the address of the task descriptor. The second is a string that holds a name for the task. You can give it a descriptive name. The third argument is the size of the stack for the new task. Passing a zero makes the function use a system dependent default. The next argument is the priority of the task. This tells the real-time scheduler how important the task is. Higher priority tasks can interrupt lower priority tasks. The last argument is the task creation mode into which you can pass bitwise OR’ed flags. For example, passing the T_JOINABLE flag allows you to call the rt_task_join() function to wait on the task to finish. In this code sample, I’m just passing in zero, which is the default mode. The function returns a 0 if the task is successfully created. Ideally, you should check for this and print an error if the return value is not zero. However, for this simple example, I’m omitting this.

A real-time task created using rt_task_create() starts off dormant. To begin the execution of the task, you need to call the rt_task_start() function.

  //Since task starts in suspended mode, start task
  rt_task_start(&loop_task, &loop_task_proc, 0);

The first two arguments are the task descriptor and a pointer to the function that implements the real-time task. The last argument is a pointer to a user defined struct that will be passed on as arguments to the real-time task function.

Finally we call the pause() function and wait for a Ctrl-C signal from the terminal.

Once rt_task_start() is called, the real-time task starts executing. To make a Xenomai task periodic, you need to call the rt_task_set_periodic() function.

 //Make the task periodic with a specified loop period
  rt_task_set_periodic(NULL, TM_NOW, LOOP_PERIOD);

If you’re calling this function from outside a real-time task, you need to pass in an RT_TASK as the first argument. However, you can also call this function from inside a real-time task with a NULL first argument. TM_NOW tells Xenomai to start timing the task right away and LOOP period is the period of the task in ticks of the clock. Since the default resolution of the clock is 1 nanosecond, this argument is the same as the period you want for the task expressed in nanoseconds.

Now we can start the infinite loop of the task.

//Start the task loop
  while(1){
    printf("Loop count: %d, Loop time: %.5f ms\n", ctr, (rt_timer_read() - tstart)/1000000.0);
    ctr++;
    rt_task_wait_period(NULL);
  }

In the loop I increment a simple counter and also use the rt_timer_read() function to get the current system time so I can print to the terminal and check if the task is running in real-time. The rt_task_wait_period() blocks the loop till the start of the next period.

When I started out trying to compile and run Xenomai with no prior experience, it seemed like quite a daunting task. The Xenomai documentation although excellent is written for programmers and as a result, it can be difficult to write your very first program. However, once you do write your very first program and you get a good idea for how it works, things go very smoothly. This post, like the one before is a bit long but hopefully, someone trying to get started with Xenomai for the first time will find it useful!

Correspondence

Reader Comments & Discussion

rational_ash ·

Glad you found it useful! :)

Kamil Bahadir ·

Hello, which compiler are you using?

Regards kamil

rational_ash ·

I'm using gcc.

rational_ash ·

However, it is better to use "xeno-config --cc" to automatically get the preferred C compiler by xenomai. This is what I've done in the Makefile.

Chisco Garcia Gonzalez ·

Thanks a lot, you saved my ass. I fully agree with you on the documentation topic

liurujia ·

Thanks a lot about the detailed introduction of xenomai.It is very useful for beginners. I want to read more documents about how to use xenomai ,but the document I found is hard for me to understand.Can you help me? My email is 1131970238@qq.com.Thank you .

fan ·

Great code! It helped me a lot in trying to understand Xenomai. Can we hope for a sequel about the concurrency problem?

biggerfan ·

Nice blog! I really like, how you explain things, it makes it all so easy to understand! As fan commented, I also am wondering about the concurrency problem in xenomai and how to solve it and I'm sure, you've already figured it out ;)

rational_ash ·

Hi! Glad that the article was useful to you. If by the "concurrency problem" you're referring to passing data between real-time and non real-time threads, I've mostly made use of Xenomai's message pipes. Since my use case usually just involves a single real-time thread that reads and writes to robot control hardware, I have not delved too deeply into Xenomai's more complicated thread synchronization features. Also I've been using less of Xenomai and more of RT Linux (PREEMPT_RT) lately since Xenomai's dual kernel structure and the need to port drivers to RTDM is a lot of work. :P

rational_ash ·

Thanks! Glad the article was useful! Refer to my reply to the comment by "biggerfan" regarding concurrency.

Join the Discussion

Kept private
Live Preview Markdown & LaTeX

Start typing to see how your comment will render.

Comments are moderated and will appear after review.

© 2025 Ashwin Narayan. All rights reserved.