Real-time Linux (Xenomai)

 Radboud University Nijmegen

Exercise #3: Semaphores


Introduction

Semaphores are useful to coordinate the activities of multitasking applications. The most obvious way for tasks to communicate is via various shared data structures. Because all tasks inside a single process exist in a single linear address space, sharing data structures between tasks is trivial. Global variables, linear buffers, ring buffers, link lists, and pointers can be referenced directly by code running in a different task context. However, while shared address space simplifies the exchange of data, interlocking access to memory is crucial to avoid data corruption. One of the methods to ensure exclusive access to shared resources is the semaphore mechanism. In addition, semaphores can be used for task synchronization.


Objectives

The following are the primary objectives of this exercise:

  • To get some experience with the use of semaphores in Xenomai.

Description

The semaphores in Xenomai provide fast intertask communication. Semaphores are the primary means for addressing the requirements of both mutual exclusion and task synchronization. In general we can say: 

  • The interface of a semaphore consists of two atomic operations, V and P, which affect an internal counter associated with the semaphore. 
  • The 'V' (signal, release, give) operation increments the counter and returns immediately.
  • The 'P' operation (wait, acquire, take) decrements the counter and returns immediately, unless the counter is already zero and the operation blocks until another process releases the semaphore. 
  • A semaphore in Xenomai is counting semaphore which can be used to guard multiple instances of a resource, this in contrast to a binary semaphore which only uses the values 0 and 1. A binary semaphore is called "mutex" in Xenomai and has a separate API which will be treated in exercise 8.

Semaphore API

See the Counting semaphore services of the Native Xenomai API Module of the Xenomai API.

  • Create a counting semaphore :
int rt_sem_create (RT_SEM *sem, const char *name, unsigned long icount, int mode) 

where icount is the initial value of the counter and mode can, for instance, be S_FIFO or S_PRIO which makes tasks pend on the semaphore in FIFO order or in priority order, respectively. (See the Xenomai API for more details.)

  • Take a semaphore using an optional timeout : 
int rt_sem_p (RT_SEM *sem, RTIME timeout)

 P from the Dutch "Proberen" meaning "try-to-decrease".

  • Give or signal a semaphore : 
int rt_sem_v (RT_SEM *sem)

V from the Dutch "Verhogen" meaning  "increment".

  • Broadcast a semaphore : signaling a semaphore, unblocks all tasks waiting on it.
int rt_sem_broadcast (RT_SEM *sem)
  • Delete a semaphore
int rt_sem_delete (RT_SEM *sem)

Example: Changing a global variable by two tasks

In the example below, two tasks (taskOne and taskTwo), are competing to update the value of a global variable, called "global." The objective of the program is to toggle the value of the global variable (1s and 0s). TaskOne increments the value of "global" and taskTwo decrements the value.

-------------------------------------------------------------------------------------
#include <stdio.h>

#include <signal.h>

#include <unistd.h>

#include <sys/mman.h>

 

#include <native/task.h>

#include <native/timer.h>

#include <native/sem.h>

 

#include  <rtdk.h>

 

#define ITER 10

 

static RT_TASK  t1;

static RT_TASK  t2;

 

int global = 0;

 

void taskOne(void *arg)

{

    int i;

    for (i=0; i < ITER; i++)

    {

        rt_printf("I am taskOne and global = %d................\n", ++global);

    }

}

 

void taskTwo(void *arg)

{

    int i;

    for (i=0; i < ITER; i++)

    {

        rt_printf("I am taskTwo and global = %d----------------\n", --global);

    }

}

 

int main(int argc, char* argv[])

{

    /* Perform auto-init of rt_print buffers if the task doesn't do so */

    rt_print_auto_init(1);

 

    /* Avoids memory swapping for this program */

    mlockall(MCL_CURRENT|MCL_FUTURE);

 

    /* create the two tasks */

    rt_task_create(&t1, "task1", 0, 1, 0);

    rt_task_create(&t2, "task2", 0, 1, 0);

 

    /* start the two tasks */

    rt_task_start(&t1, &taskOne, 0);

    rt_task_start(&t2, &taskTwo, 0);

 

    return 0;

}

-------------------------------------------------------------------------------------


Exercise

Exercise 3a.

Run the example program above and explain the resulting output.

Exercise 3b.

Change the program by using a semaphore

        RT_SEM semGlobal; 

such that variable "global" toggles between 0 and 1. Describe why your solution works.

Exercise 3c.

Use semaphores to adapt the program from exercise 2c such that, with the same priority assignment, the tasks are executed in the order of their priority (instead of the order in which they are started). Try two solutions, one which uses rt_sem_v and another with rt_sem_broadcast, where semaphores are used to signal the start of a task.


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