VII. Premiers pas▲
VII-A. Un grand classique : « Hello world! »▲
Dans l'exemple ci-dessous, nous allons créer une tâche Xenomai qui affiche un message. Cela n'a aucun intérêt d'un point de vue temps réel me direz-vous. C'est vrai, mais cela permet tout de même de valider la chaîne de compilation et le support de Xenomai dans le noyau à travers un exemple basique.
Voici le code source que nous allons compiler :
#include <stdio.h>
#include <sys/mman.h>
#include <native/task.h>
static
void
task
(
void
*
arg)
{
/* Here, we are in primary mode... */
printf
(
"
Hello world!
\n
"
);
/* Outch... printf accesses a Linux ressource so we are now
in secondary mode */
}
int
main (
void
)
{
RT_TASK task_desc;
/* disable memory swap */
mlockall
(
MCL_CURRENT |
MCL_FUTURE );
if
(
rt_task_spawn
(
&
task_desc, /* task descriptor */
"
my task
"
, /* name */
0
/* 0 = default stack size */
,
99
/* priority */
,
T_JOINABLE, /* needed to call rt_task_join after */
&
task, /* entry point (function pointer) */
NULL
/* function argument */
)!=
0
)
{
printf
(
"
rt_task_spawn error
\n
"
);
return
1
;
}
/* wait for task function termination */
rt_task_join
(&
task_desc);
return
0
;
}
Le mécanisme de pagination abstrait l'adressage mémoire. Cela permet de disposer de plus de mémoire vive qu'en réalité, la différence virtuel/réel étant comblée par une mémoire plus lente (disque dur). Dans une application temps réel, il est inconcevable d'avoir une variation dans les temps d'accès mémoire, c'est pourquoi le swap doit impérativement être désactivé en appelant mlockall.
VII-B. La compilation▲
On va simplement chercher le répertoire contenant les en-têtes et la bibliothèque :
[dchabal@localhost tuto]$
gcc -I/usr/xenomai/include hello.c -L/usr/xenomai/lib -lnative -o hello
Par la suite nous verrons une méthode de compilation plus portable.
VII-C. L'exécution▲
[dchabal@localhost tuto]$
./hello
./hello: error while
loading shared libraries: libnative.so.1
: cannot open shared object file: No such file or directory
[dchabal@localhost tuto]$
export LD_LIBRARY_PATH
=
$LD_LIBRARY_PATH
:/usr/xenomai/lib
[dchabal@localhost tuto]$
./hello
Xenomai: binding failed: Operation not permitted.
[dchabal@localhost tuto]$
Qu'est-ce qui ne va pas ici ?
La première erreur vient du fait que la bibliothèque libnative.so.1 est chargée dynamiquement et que son chemin ne se trouve pas dans la variable d'environnement LD_LIBRARY_PATH.
On peut également ne pas toucher au LD_LIBRARY_PATH et modifier le fichier /etc/ld.so.conf puis lancer ldconfig (en tant que root), comme ceci:
[root@localhost tuto]# more /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/X11R6/lib
/usr/lib/qt3/lib64
/usr/xenomai/lib
[root@localhost tuto]# ldconfig
[root@localhost tuto]#
La seconde erreur est qu'il faut avoir les droits root pour lancer les applications Xenomai, d'où le message d'opération non autorisée.
En tant que root, on a bien le résultat escompté :
[root@localhost tuto]# ./hello
Hello world!
[root@localhost tuto]#
Le temps d'accès à la mémoire doit être constant. Or, ce n'est pas garanti lorsque le swap disque est activé.
Il faut donc impérativement le désactiver avec la fonction mlockall.
L'inconvénient est que l'application aura moins de ressources mémoire.
Il faudra être particulièrement vigilant lors de l'appel aux fonctions de type malloc (secondary mode) en contrôlant le code de retour.
VII-D. Que se passe-t-il ?▲
Les commandes top, fuser... existaient sous Linux afin de surveiller l'état du système, comment fait-on maintenant pour surveiller nos nouvelles tâches ?
Les variables internes de Xenomai sont accessibles dans le répertoire /proc/xenomai(34). L'arborescence est alors la suivante :
affinity
CPU utilisés pour les tâches Xenomai. Exemple: 00000003, soit en binaire 0000.0011 pour le dernier octet, correspondant aux CPU0 et CPU1 sur un Intel Dual Core ;
apc
L'APC est le mécanisme qui optimise de la prise en compte des handlers d'interruption Linux en attente lorsque Linux reprend la main ;
faults
Liste les erreurs levées par processeur ;
hal
Version d'Adeos ;
heap
Size of the private stack pool (comme son nom ne l'indique pas) internes configurés dans Size of the system heap et Size of the private stack pool.
interfaces/
Nombre d'applications Xenomai en cours d'exécution, classées par skins ;
native ;
rtdm ;
sys ;
irq
Comptage du nombre d'interruptions utilisées par Xenomai ;
latency
Latence du scheduler (expliqué dans Scheduling latency) ;
registry/
Liste des objets temps réel gérés par Xenomai ;
rtdm/
Informations concernant les drivers temps réel ;
fildes
Liste des descripteurs (attention contrairement à Linux, les descripteurs commencent à 0) ;
named_devices
Liste des périphériques, rtserx par exemple pour les « ports série temps réel ». Alter ego du /dev Linux ;
open_fildes
Liste des descripteurs pointant un périphérique utilisé ;
protocol_devices
Liste des protocoles utilisés par les périphériques (CAN) ;
rttest0/ ;
information
Informations (version, type de drivers...) disponibles pour chaque périphérique présent dans le répertoire RTDM. rttest0 correspond ici à un driver virtuel dédié au test ;
rttest1/ ;
information ;
rttest2/ ;
information ;
sched
Liste les tâches dans le scheduler Xenomai (voir ci-dessous) ;
stat
Statistiques (utilisation CPU...) collectées si l'option du noyau est activée ;
timebases
États des timers des différentes skins ;
timer
État du timer, exemple :
status=on+watchdog:setup=0:clock=379818707114:timerdev=lapic:clockdev=tsc
status : le timer est actif et le watchdog de débogage est activé ;
setup : latence du timer en nanosecondes ;
clock :nombre de ticks émis par clockdev écoulés depuis la mise sous tension du système ;
timerdev : timer utilisé (lapic) ;
clockdev : horloge utilisée (time stamp counter : timer haute résolution implémenté dans le CPU) ;
timestat/
Indications sur la programmation des timers ;
version
Version de Xenomai.
L'état d'ADEOS est également consultable dans le répertoire /proc/ipipe comme nous l'avons vu précédemment.
Nous avions dit qu'une tâche en secondary mode était dans le scheduler Linux, il est temps maintenant de le vérifier. En insérant un appel à pause() après notre printf(), nous retrouvons bien notre tâche via un ps :
[dchabal@localhost xenomai]$ ps -eTo pid,ppid,ucmd,class,rtprio,nice --sort class
PID PPID CMD CLS RTPRIO NI
1 0 init TS - 0
...
4059 3926 hello TS - 0
4059 3926 my task FF 99 -
[dchabal@localhost xenomai]$
Nous avons donc bien notre tâche Linux principale (code du main) qui s'appelle hello et qui est dans la classe SCHED_OTHER (TS pour time share), et notre tâche Xenomai en secondary mode "my task" qui a conservé sa priorité 99. Elle est bien dans la classe SCHED_FIFO (FF, on aurait eu RR pour SCHED_RR).
Et si maintenant on jette un œil du côté du scheduler Xenomai :
[dchabal@localhost xenomai]$
cat /proc/xenomai/sched
CPU PID PRI PERIOD TIMEOUT TIMEBASE STAT NAME
0
0
-1
0
0
master R ROOT/0
1
0
-1
0
0
master R ROOT/1
0
4059
99
0
0
master X my task
[dchabal@localhost xenomai]$
On retrouve encore notre tâche "my task", marquée d'un 'X', c'est-à-dire en secondary mode.
La tâche ROOT est Linux en mode idle (priorité symboliquement notée -1).
Le tableau ci-dessous(35) liste les statuts possibles :
S | Forcibly suspended. |
w/W | Waiting for a resource, with or without timeout. |
D | Delayed (without any other wait condition). |
R | Runnable. |
U | Unstarted or dormant. |
X | Relaxed shadow. |
H | Held thread. |
b | Priority boost undergoing. |
T | Ptraced and stopped. |
l | Locks scheduler. |
r | Undergoes round-robin. |
s | Interrupt shield enabled. |
t | Mode switches trapped. |
o | Priority coupling off. |
f | FPU enabled (for kernel threads). |