Vous êtes sur la page 1sur 59

Exemple : création et activation d’une tâche (one-shot)

Dans cet exemple il s’agit de créer et de lancer une tâche qui exécute
une séquence simple d’instructions. La tâche est ensuite détruite
naturellement quand le pme ppal qui l’a crée se termine. Par
exemple, créer une tâche de forte priorité qui affiche « hello World ».
Créer la tâche dans le programme principal avec le mode T_JOINABLE
et attendre que la tâche ait terminé son exécution avec la fonction
rt_task_join(&task_desc).
Utiliser la fonction rt_task_spawn () pour créer et activer la tâche.

Penser à récupérer systématiquement le code retourné par la fonction


de création de la tâche afin de s’assurer que cela s’est bien passé.

Rque : créer une tâche Xenomai qui affiche un message n'a aucun
intérêt d'un point de vue temps réel mais permet simplement de
présenter la structure d’une application à travers un exemple basique.
#include <stdio.h>
#include <sys/mman.h>
#include <native/task.h>
RT_TASK task_desc;
void task (void *cookie) /* ... "cookie" should be NULL ... */
{
printf("Hello world !\n");
}
int main (void)
{
int err;

//disable memory swap 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
mlockall( MCL_CURRENT | MCL_FUTURE ); le désactiver avec la fonction mlockall.
//create task 'mytask'
if ((err=rt_task_spawn(
&task_desc, //descripteur de la tâche
le programme principal va attendre que la tâche "my task" se
"my task", //nom de la tâche termine grâce à la fonction rt_task_join.
0, //taille de la pile, si 0, autogéré Pour que la tâche soit suspendue au moment de son démarrage,
99, //priorité (0 = faible, 99 = forte) il faut mettre T_SUSP. Il faudra ensuite la redémarrer avec la
T_JOINABLE, //indique qu’on attend la fin de la tâche fonction rt_task_resume.
&task, //la fonction a exécuter
NULL //pas de paramètres de la fonction
))!=0)
{
fprintf (stderr,"rt_task_spawn error %d\n",err);
return 1;
}
//wait for the function to complete
rt_task_join(&task_desc);
return 0;
}
Exemple ( variante) : création et activation d’une tâche
avec passage de paramètre à l’appel

Reprenons l’exemple précédent avec une variante : au lieu


de prédéfinir la chaîne de caractère à afficher dans la tâche
(‘Hello World’), on la lui passe en argument.

On applique les bonnes pratiques de programmation et on


note symboliquement les arguments des fonctions.
#include <stdio.h>
#include <sys/mman.h>
#include <native/task.h>

#define TASK_PRIO 99 /* Highest RT priority */


#define TASK_MODE T_JOINABLE
#define TASK_STKSZ 0 /* Stack size (use default one) */

RT_TASK task_desc;

void task_body (void *cookie) {


printf("%s\n", (char*) cookie);
}

int main (int argc, char**argv)


{
int err;
//disable memory swap
mlockall( MCL_CURRENT | MCL_FUTURE );

char my_string[50] = "Hello world !";

err = rt_task_spawn
(&task_desc, "MyTaskName", TASK_STKSZ, TASK_PRIO, TASK_MODE, &task_body , my_string);

if(!err) { rt_task_join(&task_desc); }

return 0;
Exemple : création, activation et destruction d’une tâche

Dans cet exemple il s’agit de créer et de lancer une tâche qui


s'exécute mais ne se termine pas, puis de la détruire quand
l’utilisateur le souhaite. Par exemple, cette tâche, de forte priorité,
affiche « hello World » (voir premier exemple) puis se met en
sommeil indéfiniment (rt_task_sleep_until(TM_INFINITE)).

NB : Utiliser pour changer les fonctions rt_task_create () et


rt_task_start () pour respectivement créer et activer la tâche.
Penser à récupérer systématiquement le code retourné par les
fonctions.
On applique toujours les bonnes pratiques de programmation et on
note symboliquement les arguments des fonctions.

Définir spécifiquement une fonction de destruction de la tâche en


sommeil qui sera appelée par l’utilisateur.
#include <sys/mman.h>
#include <native/task.h>
#include <unistd.h>
#include <stdio.h>

#define TASK_PRIO 99 /* Highest RT priority */


#define TASK_MODE T_JOINABLE
#define TASK_STKSZ 0 /* Stack size (use default one) */

RT_TASK task_desc;

Rque : quand on fait un printf () on accède aux ressources de


void task_body (void *cookie) {
linux, la tâche passe alors de primary mode ) secondary mode.
printf("hello world !"); On préfèrera utiliser le rt_printf ()
/* Wait infinitely */
rt_task_sleep_until (TM_INFINITE);
}

int main (int argc, char**argv) {


int err;
/* disable memory swap */
mlockall (MCL_CURRENT|MCL_FUTURE);
err = rt_task_create (&task_desc,"MyTaskName", TASK_STKSZ, TASK_PRIO, TASK_MODE);
if(!err) { rt_task_start (&task_desc, & task_body, NULL); }
if(!err) { rt_task_join(&task_desc); }
}

void cleanup (void) {


/* cancellation of the task */
rt_task_delete (&task_desc); // il faudrait récupérer le retour de fonction aussi….
}
#include <sys/mman.h>
#include <native/task.h> Terminaison adoptée dans le TP
#include <unistd.h>
#include <stdio.h>

#define TASK_PRIO 99 /* Highest RT priority */


#define TASK_MODE 0 /* No flags*/
#define TASK_STKSZ 0 /* Stack size (use default one) */

RT_TASK task_desc;

void task_body (void *cookie) {


rt_printf("hello world !");
/* Wait infinitely */
rt_task_sleep_until (TM_INFINITE);
}

int main (int argc, char**argv) {


int err;
/* disable memory swap */
mlockall (MCL_CURRENT|MCL_FUTURE);
err = rt_task_create (&task_desc,"MyTaskName", TASK_STKSZ, TASK_PRIO, TASK_MODE);
if(!err) { rt_task_start (&task_desc, & task_body, NULL); }

pause; // équivalent à rt_task_sleep_until (TM_INFINITE);


/* cancellation of the task */
rt_task_delete (&task_desc);

}
Exemple : création, activation et destruction de plusieurs
tâches – illustration de la réentrance
Créer trois tâches de même priorité, « poignet »,
« coude » et « épaule » qui vont piloter en temps
réel et de façon autonome et indépendante le
déplacement d’un bras de robot anthropomorphe.
Le mouvement du bras résulte de la coordination
fine des mouvements des trois articulations.

Coude, épaule et poignet fonctionnent sur le


même principe.

NB :
Utiliser la fonction rt_task_spawn () pour créer et activer les tâches.
Utiliser la terminaison adoptée dans le TP.
#define TASK_PRIO 99 /* Highest RT priority */
#define TASK_MODE 0 /* No flags */
#define TASK_STKSZ 0 /* Stack size (use default one) */

RT_TASK t_Coude, t_Poignet, t_Epaule;

Void Deplacer_Articulation (….) {


/* assure le déplacement de l’articulation vers la position à rejoindre */
}

int main (int argc, char**argv) {


/* disable memory swap */
mlockall (MCL_CURRENT|MCL_FUTURE);

rt_task_spawn(&t_Coude, “Coude", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , NULL);


rt_task_spawn(&t_Poignet, “Poignet", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , NULL);
rt_task_spawn(&t_Epaule, “Epaule", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , NULL);

pause;

/* cancellation of the task */


rt_task_delete (& t_Coude);
rt_task_delete (& t_Poignet);
rt_task_delete (& t_Epaule);
}
Avec passage de paramètre en entrée
RT_TASK t_Coude, t_Poignet, t_Epaule;

Double pos_coude, pos_poignet, pos_epaule;


Il faut communiquer la position à rejoindre !
Void Deplacer_Articulation (void * pos_articulation) {
/* assure le déplacement de l’articulation vers la position à rejoindre */
}

int main (int argc, char**argv) {


/* disable memory swap */
mlockall (MCL_CURRENT|MCL_FUTURE);

rt_task_spawn(&t_Coude, “Coude", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation ,


&pos_coude);
rt_task_spawn(&t_Poignet, “Poignet", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation ,
&pos_poignet);
rt_task_spawn(&t_Epaule, “Epaule", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation ,
&pos_epaule);

pause;

/* cancellation of the task */


rt_task_delete (& t_Coude);
rt_task_delete (& t_Poignet);
rt_task_delete (& t_Epaule);
}
Ordonnancement préemptif basé priorité

Avec Round Robin


Exemple : créer et activer une tâche périodique

Reprendre l’exemple du bras de robot et rajouter un


capteur de position sur chaque articulation, qui donne à
chaque seconde la position angulaire de l’articulation.
#define … Mécanisme
RT_TASK t_Coude, t_Poignet, t_Epaule, t_Capt_Coude, t_Capt_Epaule, t_Capt_Poignet;
double pos_coude, pos_epaule, pos_poignet;

void Deplacer_Articulation (void * pos_articulation) {/* assure le déplacement de l’articulation vers la position à
rejoindre */ }

void Capturer_Pos (void * pos_articulation) {


if ( rt_task_set_periodic (NULL, /* the current task or rt_task_self() */
TM_NOW, /* delay before release, TM_NOW = none */
ONE_SECOND /* this value is in nanosec */
) != 0)
{ printf("rt_task_set_periodic error\n"); }
while (1) {
rt_task_wait_period(NULL); /* Attente de l'activation périodique sans détection d’overruns */
/* lire la position courante pos_articulation et la mettre a jour dans l’application*/
}
}
int main (int argc, char *argv[]) {
mlockall (MCL_CURRENT|MCL_FUTURE);
rt_task_spawn(&t_Coude, “Coude", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , pos_coude);
….
/* puis creer et activer les trois capteurs à l’identique*/
rt_task_spawn(&t_Capt_Coude, “Capt_Coude", TASK_STKSZ, TASK_MODE, & Capturer_Pos, pos_coude);

rt_task_sleep_until (20*ONE_SECOND);
/* cancelation of all the tasks */
……
}
#define … Avec passage de paramètre
RT_TASK t_Coude, t_Poignet, t_Epaule, t_Capt_Coude, t_Capt_Epaule, t_Capt_Poignet;
double pos_coude, pos_epaule, pos_poignet;

void Deplacer_Articulation (void * pos_articulation) {/* assure le déplacement de l’articulation vers la position à
rejoindre */ }

void Capturer_Pos (void * pos_articulation) {


if ( rt_task_set_periodic (NULL, /* the current task or rt_task_self() */
TM_NOW, /* delay before release, TM_NOW = none */
ONE_SECOND /* this value is in nanosec */
) != 0)
{ printf("rt_task_set_periodic error\n"); }
while (1) {
rt_task_wait_period(NULL); /* Attente de l'activation périodique sans détection d’overruns */
/* lire la position courante pos_articulation et la mettre a jour dans l’application*/
}
}
int main (int argc, char *argv[]) {
mlockall (MCL_CURRENT|MCL_FUTURE);
rt_task_spawn(&t_Coude, “Coude", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , pos_coude);
….
/* puis creer et activer les trois capteurs à l’identique*/
rt_task_spawn(&t_Capt_Coude, “Capt_Coude", TASK_STKSZ, TASK_MODE, & Capturer_Pos, pos_coude);

rt_task_sleep_until (20*ONE_SECOND);
/* cancelation of all the tasks */
……
}
Exemple : créer et activer une tâche périodique

Variante incluant la détection de l'overrun de la tâche


périodique, c'est-à-dire le nombre de fois où le réveil de la
tâche a échoué car elle était encore en cours d'exécution.
P?

V!
P?
In the example below, two tasks (taskOne and taskTwo, same priority, no round robin), 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 changes the value of "global" to 1 and taskTwo changes the value back to 0.
=> Trace the program execution

/* globals */
#define ITER 10
SEM_ID semBinary;
int global = 0;

void binary(void)
{
int taskIdOne, taskIdTwo;
/* create semaphore with semaphore available and queue tasks on FIFO basis */
semBinary = semBCreate(SEM_Q_FIFO, SEM_FULL);

/* spawn the two tasks */


taskIdOne = taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,0,0,0);
taskIdTwo = taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,0,0,0);
}
void taskOne(void)
{
int i;
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskOne and global = %d......................\n", ++global);
semGive(semBinary); /* give up semaphore */
}
}
void taskTwo(void)
{
int i;
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskTwo and global = %d----------------------\n", --global);
semGive(semBinary); /* give up semaphore */
}
}
Correction

/* globals */
#define ITER 10
SEM_ID semBinary;
int global = 0;

void binary(void)
{
int taskIdOne, taskIdTwo;
/* create semaphore with semaphore available and queue tasks on FIFO basis */
semBinary = semBCreate(SEM_Q_FIFO, SEM_FULL);
/* Note 1: lock the semaphore for scheduling purposes */
semTake(semBinary,WAIT_FOREVER);
/* spawn the two tasks */
taskIdOne = taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,0,0,0);
taskIdTwo = taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,0,0,0);
}
void taskOne(void)
{
int i;
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskOne and global = %d......................\n", ++global);
semGive(semBinary); /* give up semaphore */
}
}
void taskTwo(void)
{
int i;
semGive(semBinary); /* Note 2: give up semaphore(a scheduling fix) */
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskTwo and global = %d----------------------\n", --global);
semGive(semBinary); /* give up semaphore */
}
}
Flush ??

Protection en destruction ???


Problème : inversion de priorité

Solution : héritage de priorité


Interblocage
RT_TASK task_desc1, task_desc2;
RT_MUTEX mutex_desc;

void t_task1 (void *cookie) {


/* grab the mutex lock, run the critical section, then release the lock */
rt_mutex_acquire(&mutex_desc, TM_INFINITE);
/* ... Critical section ... */
rt_mutex_release(&mutex_desc);
/* ... */
}
void t_task2 (void *cookie) {
/* grab the mutex lock, run the critical section, then release the lock */
rt_mutex_acquire(&mutex_desc, TM_INFINITE);
/* ... Critical section ... */
rt_mutex_release(&mutex_desc);
/* ... */
}

int main (int argc, char**argv)


{ int err;
//create tasks : ….
// create mutex
err = rt_mutex_create(&mutex_desc,"MyMutex");
pause;
// delete tasks : ….
/* delete mutex */
rt_mutex_delete(&mutex_desc);
}
Exemple : utilisation d’un mutex

Reprendre l’exemple du bras de robot et rajouter la


protection des variables de position des articulations
RT_TASK t_Coude, t_Poignet, t_Epaule, t_Capt_Coude, t_Capt_Epaule, t_Capt_Poignet;
RT_MUTEX mutex_coude, mutex_epaule, mutex_poignet;
double pos_coude, pos_epaule, pos_poignet;

void Deplacer_Articulation (void * args) {/* assure le déplacement de l’articulation vers la position à rejoindre */
rt_mutex_acquire(&mutex_pos, TM_INFINITE);
/* lire la position courante pos_articulation */
rt_mutex_release(&mutex_pos);
/* et actionner l’articulation*/}

void Capturer_Pos (void * args) { /* passer une structure avec pos_articulation et mutex_pos qui la protège */
if ( rt_task_set_periodic (NULL, /* the current task or rt_task_self() */
TM_NOW, /* delay before release, TM_NOW = none */
ONE_SECOND /* this value is in nanosec */
) != 0)
{ printf("rt_task_set_periodic error\n"); }
while (1) {
rt_task_wait_period(NULL); /* Attente de l'activation périodique sans détection d’overruns */
rt_mutex_acquire(&mutex_pos, TM_INFINITE);
/* lire la position courante pos_articulation et la mettre a jour dans l’application*/
rt_mutex_release(&mutex_pos);
}
}
int main (int argc, char *argv[]) {
mlockall (MCL_CURRENT|MCL_FUTURE);
rt_task_spawn(&t_Coude, “Coude", TASK_STKSZ, TASK_MODE, & Deplacer_Articulation , /*structure*/);
….
rt_task_spawn(&t_Capt_Coude, “Capt_Coude", TASK_STKSZ, TASK_MODE, & Capturer_Pos, /*structure*/);

rt_mutex_create (&mutex_coude,“Mutex_Coude");
……
}
Communication par file de messages

Absorption différence rythme


Stockage des données sans écrasement
Synchronisation
Exclusion mutuelle
Priorisation des messages à l’envoi
Gestion des émetteurs/récepteurs en attente

Exemple : mailbox sous Gmail


Plusieurs émetteurs
Plusieurs récepteurs
Comm. Un à Un ou broadcast

Unidirectionnel …

Pas de destinataire
identifié
1. Création de la file Mécanisme

Timeout :
Q_URGENT/Q_NORMAL/Q_BROADCAST TM_INFINITE/TM_NONBLOCK

3. Envoi 4. Réception
( )
FIFO /PRIO
2. Réservation d’un espace
et écriture du message 5. Désallocation
size q_limit

suite
6. Destruction de la file
Création de la file

int rt_queue_create ( RT_QUEUE* q, address of a queue descriptor


const char * name, ASCII string - symbolic name
size_t poolsize, size (bytes) of the message buffer pool - fixe
size_t qlimit, maximum number of messages / Q_UNLIMITED
int mode ); mode :
• Q_FIFO makes tasks pend in FIFO order
on the queue for consuming messages.
• Q_PRIO makes tasks pend in priority
order on the queue.

Create a message queue object that allows multiple tasks to


exchange data through the use of variable-sized messages.
A message queue is created empty.
retour
Réservation d’un espace puis écriture du message

void rt_queue_alloc ( RT_QUEUE* q, address of a queue descriptor


size_t size ); requested size in bytes of the buffer

Allocate a message queue buffer from the queue's internal pool


which can be subsequently filled by the caller (memcpy() ou
strcopy()) then passed to rt_queue_send() for sending.

retour
Envoi

int rt_queue_send ( RT_QUEUE* q, address of a queue descriptor


void *mbuf, address of the message buffer to be sent*
size_t size, size in bytes of the message
int mode ); (0: an empty message will be sent)

mode :
• Q_URGENT : message prepended to the
message queue (LIFO ordering)
• Q_NORMAL : message appended to the
message queue (FIFO ordering)
• Q_BROADCAST : message sent to all
tasks currently waiting for messages.
Send a message to a queue.
The message must have been allocated by a previous call to
rt_queue_alloc().
Returns the number of receivers which got awaken retour
as a result of the operation
Réception

ssize_t rt_queue_receive ( RT_QUEUE* q,


void **bufp, pointer to a memory
RTIME timeout ); location which will be
written upon success with
the address of the received
message.
Timeout : number of clock ticks to wait for a message to arrive.
• TM_INFINITE : caller blocked indefinitely until some message is eventually available
• TM_NONBLOCK : no waiting if no message is available

Receive a message (next available) from a queue.


Unless otherwise specified, the caller is blocked for a given amount
of time if no message is immediately available on entry.
The number of bytes available from the received message is
returned upon success
retour
Désallocation
Once consumed, the message space should be freed using rt_queue_free().

void rt_queue_free ( RT_QUEUE* q,


void *buf );

Releases a message buffer returned by rt_queue_receive() to the


queue's internal pool.

retour
Destruction de la file

int rt_queue_delete ( RT_QUEUE* q); address of a queue descriptor

Delete a message queue.


Destroy a message queue and release all the tasks currently
pending on it.
A queue exists in the system since rt_queue_create() has been
called to create it, so this service must be called in order to destroy
it afterwards.

retour
Exemple : deux tâches s’envoient en boucle des messages
par une file de message

Jouez avec les différents paramètres :


- Priorités et ordonnancement
- Taille de la file et timeouts
- Gestion
- Tailles des messages R/W
RT_TASK desc_envoi, desc_reception;
RT_QUEUE queue;

#define MSG_QUEUE_SIZE 10 /* nbre max de messages dans la file */

void t_envoi (void *arg)


{ DMessage dataAEnvoyer = …; /* à faire en boucle */
void *msg;
int err;
msg = rt_queue_alloc (&queue, sizeof (DMessage));
2 //on copie le message a l'endroit ou pointe *msg
memcpy (msg, &dataAEnvoyer, sizeof (DMessage));
//Q_NORMAL :a la file, Q_URGENT : en debut de file
3 if ((err = rt_queue_send (&queue, msg, sizeof (DMessage), Q_NORMAL)) < 0) /* posté FIFO*/

{
rt_printf("Error msg queue send: %s\n", strerror(-err));
}
}
void t_reception (void *arg)
{ DMessage *msg; /* à faire en boucle */
4 int err;
if ((err = rt_queue_receive (&queue ,&msg, TM_INFINITE)) > 0) /* timeout bloquant*/
{
5 rt_printf("message recu\n");
rt_queue_free (&queue, msg);
}
}
int main (int argc, char *argv[])
{
int err;
mlockall(MCL_CURRENT|MCL_FUTURE);
err = rt_task_create (………….t_envoi …………..);
err2 = rt_task_create (………….t_reception…………..);

/* Creation de la file de messages : gestion des files d’attente fifo ou prio*/


1 if (err = rt_queue_create (&queue, « »ma_file", MSG_QUEUE_SIZE*sizeof(DMessage), /*pool*/
MSG_QUEUE_SIZE, /*nbre max messages*/
Q_FIFO)) / * gestion de la file des tâches bloquées en lecture*/
{
rt_printf("Error msg queue create: %s\n", strerror(-err));
}
if (!err)
rt_task_start( t_envoi……);
if (!err2)
rt_task_start (t_reception ……..);
/* ... */
pause;

rt_task_delete (desc_envoi);
rt_task_delete (desc_reception);
*/ détruire la file */
6 rt_queue_delete ( &queue);
}
Sémaphores à compteur : construction
Sémaphores à compteur : fonctionnement
rt_sem_p ( ); (recursive)
count = count+1
rt_sem_p ( );
count = count+1

rt_sem_create ( );

rt_sem_v ( );
count = count-1
rt_sem_v ( ); (recursive)
count = count-1

rt_sem_create ( );
rt_sem_p ( )
rt_sem_p ( )
rt_sem_p ( )
rt_sem_p ( )
rt_sem_v ( )
rt_sem_v ( )
Sémaphores à compteur : opérations
Application 1 : Synchronisation par sémaphores

rt_sem_create(…);

rt_sem_v (Dispem,…);

rt_sem_v() rt_sem_p()

rt_sem_p (Dispem…);
Application 1 : Synchronisation par sémaphores

Synchronisation multiple :
Exemple de synchronisation
EXEMPLE 1 : T1 doit attendre que T2 et T3 aient terminé
RT_SEM sem;

void T1 (void *arg) {


rt_sem_p(&sem,TM_INFINITE);
rt_sem_p(&sem,TM_INFINITE);
// T2 et T3 ont terminé
}
void T2 (void *arg) {
// traitement
rt_sem_v(&sem);
}
void T3 (void *arg) {
// traitement
rt_sem_v(&sem);
}
void main() {
rt_sem_create(&sem,"Semaphore", 0, S_PRIO);
// création des taches
}
Exemple de synchronisation
EXEMPLE 2 : T1 permet à T2 et T3 de continuer
RT_SEM sem;

void T1 (void *arg) {


// traitement
rt_sem_v(&sem);
rt_sem_v(&sem);
}
void T2 (void *arg) {
rt_sem_p(&sem,TM_INFINITE);
// traitement
}
void T3 (void *arg) {
rt_sem_p(&sem,TM_INFINITE);
// traitement
}
void main() {
rt_sem_create(&sem,"Semaphore",0, S_PRIO);
// création des taches
}
Synchronisation par sémaphores

On veut faire un thread périodique portable, sans utiliser la fonction


pthread_make_periodic_np qui est spécifique de Xenomai. L’application
découplera les fonctionnalités « cadencement » et « action. Elle comprendra donc
2 threads :
un thread « métronome » lancé à la date r0 et qui va attendre les dates r0+P,
r0+2P, etc... Au réveil, il libère un sémaphore (basé sur la mémoire). Notez bien
qu'il s'agit de dates de réveil absolues, et non pas d'intervalles - qui pourraient
provoquer un glissement des dates de réveil.
le thread « périodique » qui attend le sémaphore pour effectuer son travail
Au démarrage, le thread métronome lit la valeur courante r0 de l’horloge
CLOCK_REALTIME. Il rentre ensuite dans une boucle infinie où il calcule une date
absolue de réveil r0 = r0+P, puis s’endort jusqu’à r0. Au réveil, il libère le
sémaphore.
Le thread périodique va simplement mesurer la stabilité du mécanisme. Il est dans
une boucle infinie où il commence par attendre le sémaphore libéré par le
métronome. Quand celui-ci est libéré, il lit l’heure, calcule l’intervalle de temps
depuis le dernier réveil et le compare à la période théorique P (jitter)

Voir http://francois.touchard.perso.esil.univmed.fr/IRM4/TD4/index.html
Application 2 : Exclusion mutuelle par sémaphore à compteur

Même mécanisme qu’avec un mutex ! (options différentes…)

P?

V!
P?
Exemple d’exclusion mutuelle
RT_SEM sem_desc;

void T1 (void *arg) {


rt_sem_p(&sem_desc, TM_INFINITE);
// section partagée
rt_sem_v(&sem_desc);
}
void T2 (void *arg) {
rt_sem_p(&sem, TM_INFINITE);
// section partagée
rt_sem_v(&sem);
}
void T3 (void *arg) {
rt_sem_p(&sem_desc, TM_INFINITE);
// section partagée
rt_sem_v(&sem_desc)
}
void main() {
// init. du sémaphore à SEM_INIT =1
rt_sem_create (&sem_desc,"Semaphore", SEM_INIT, S_PRIO);
// création des taches
}
Comparaison mutex/sémaphores compte
Mutex Sémaphore à compteur
Nombre de 1 seul jeton Plusieurs jetons (dépend de la
jetons valeur du compteur)
Initialisation (par défaut) Valeur =1 Compteur = valeur (0=bloquant)
Création Mutex + Nom Sémaphore + Nom + Valeur du
compteur + Mode
Destruction Adresse du mutex Adresse du sémaphore
Prise Mutex + Timeout Sémaphore + Timeout
ent
(TM_INFINITE, TM_NONBLOCK, n ) (TM_INFINITE, TM_NONBLOCK, n )
Libération Adresse du mutex Adresse du sémaphore
Gestion de la file Mode PRIO implicite Dépend du mode (S_FIFO, S_PRIO,
d’attente S_PULSE )
Utilisation Protection de section critique Synchronisation de processus
Opérations Prendre/libérer Prendre /libérer (pas le même si
synchro!)
Options Inversion de priorité -
Propriétaire Oui Non
flush
Broadcast Non Oui
Comparaison mutex/sémaphores binaires
Mutex Sémaphore binaire
Nombre de 1 seul jeton 1 seul jeton
jetons
Initialisation (par défaut) Valeur =1 valeur 1 ou 0 (0=bloquant)
Création Identifiant Mutex + Nom Identifiant Sémaphore + Nom +
(gestion file d’attente PRIO pra Valeur du compteur + Mode
défaut)
Destruction Adresse du mutex Adresse du sémaphore
ent Prise Mutex + Timeout Sémaphore + Timeout
(TM_INFINITE, TM_NONBLOCK, n ) (TM_INFINITE, TM_NONBLOCK, n )
Libération Adresse du mutex Adresse du sémaphore
Gestion de la file Mode PRIO implicite Dépend du mode (S_FIFO, S_PRIO,
d’attente S_PULSE )
Utilisation Protection de section critique Synchronisation de processus
Opérations Prendre/libérer Prendre /libérer (pas le même si
synchro!)
Options héritage de priorité, Flush interdit synchronization multiple possible
Propriétaire Oui, protection en destruction Non
Broadcast Non Oui
#define ALARM_VALUE 500000 /* First shot at now + 500 us */
#define ALARM_INTERVAL 250000 /* Period is 250 us */
RT_ALARM alarm_desc;
RT_TASK server_desc;
void server_alarm (void *cookie) {
for (;;) {
/* Wait for the next alarm to trigger. */
err = rt_alarm_wait (&alarm_desc);
if (!err) { /* Process the alarm shot. */ }
}
}
int main (int argc, char *argv[]) {
int err;
mlockall(MCL_CURRENT|MCL_FUTURE);
/* ... */
err = rt_alarm_create (&alarm_desc,"MyAlarm");
err = rt_alarm_start (&alarm_desc, ALARM_VALUE, ALARM_INTERVAL);
/* ... */
err = rt_task_create(&server_desc,"MyAlarmServer",TASK_STKSZ, TASK_PRIO, TASK_MODE);
if (!err)
rt_task_start (&server_desc, &server_alarm, NULL);
/* ... */
}
void cleanup (void) {
rt_alarm_delete (&alarm_desc);
rt_task_delete (&server_desc);
}
Alarmes : Watchdog timers
Alarms can be either one shot or periodic; in the latter case, the real-time kernel
automatically reprograms the alarm for the next shot according to a user-defined interval
value.

Periodic Execution
rt_alarm_create (&alarm_desc, “my_alarm”);
rt_alarm_start (&alarm_desc, start_time, period);
...
Alarms can be made periodic or oneshot,
void treat_alarm (int param) depending on the reload interval
{
while (1)
Basic principle:
{/* Wait for the alarm expiration */
define some alarm server task which
rt_alarm_wait(&alarm_desc);
routinely waits for the next incoming
/* Process the work */ alarm event
doIt (…..);}
}
rt_alarm_delete (&alarm_desc);
Deadline-miss Recovery
rt_alarm_create (&alarm_desc, “my_alarm”);
void Critical_periodic_task (int param)
{
while (1)
{
/* wait next release (periodic task or semaphore) */
rt_alarm_start (&alarm_desc, start_time, period);
doWork();
Pour désarmer une alarme :
rt_alarm_stop (&alarm_desc);
rt_alarm_stop (&alarm_desc);
}
}
void Deadline_detection ()
{
while (1)
{
rt_alarm_wait(&alarm_desc);
/* Handle deadline miss */
}
}

Vous aimerez peut-être aussi