L’objectif de ce travail consiste à mettre en oeuvre une politique d’ordonnnancement de type RMS puis différents types de serveurs périodiques de tâches apériodiques.
Vous pouvez suivre les liens suivants pour trouver les sources et les transparents du cours Ordonnancement Temps Réel.
Pour décompresser l’archive faire :
gunzip src.tar.gz tar xf src.tar
En termes de programmation, vous n’aurez qu’à compléter des fichiers
déjà existants (periodictasks.c
) et notamment les diverses
implémentations des serveurs de tâches apériodiques
(backgroundserver.c
, sporadicserver.c
,
pollingservers.c
, deferred_servers.c
).
Sur la forme, les morceaux de code à modifier sont indiqués par un appel à la fonction NYI (Not Yet Implemented) qui prend comme paramètre une chaîne de caractères vous indiquant le type de modification à apporter. Il est en général précédé d’un commentaire explicitant ce qu’il convient de faire.
Sur le fond, votre travail consistera à insérer le code permettant de "configurer" et "gérer" l’activité de ces serveurs de tâches apériodiques pour respecter la périodicité, le comportement des serveurs, la recherche d’évènements dans la file associée aux serveurs, le traitement des évènements.
Le TP ne requiert pas de compétences particulières en termes de programmation C ou POSIX, ou RT-POSIX.
Dans ce TP, nous vous proposons différentes configurations de tâches
logiques que vous devez faire exécuter correctement. Ces configurations
ou scénarii se trouvent décrits dans le fichier scenarii.c
. Ils
portent un numéro de 1 à 7. Lorsque l’on vous demandera de vérifier
que l’implantation d’une fonctionnalité est correcte il s’agira de
vérifier que l’un de ces scenarii se déroule correctement. Ces
différents scenarii correspondent à ceux présents dans le cours.
On sélectionnera un scenario (par exemple le scenario 3) au lancement en exécutant la commande :
make ./runapsvr 3
Lorsque vous exécutez, le programme ./runspsvr
en fournissant un
numéro de scenario en argument, les caractéristiques des tâches de
celui-ci sont rappelées en début d’exécution.
Les sources fournies vous permettent d’exécuter des tâches périodiques
sous forme de threads POSIX. Une tâche logique décrite dans le scenario
par un ensemble de données est associée à une tâche physique de l’API
POSIX (c’est à dire un thread POSIX). La procédure principale de ce
thread se trouve décrite dans runPeriodicTask
du fichier
periodictasks.c
.
Ces tâches physiques sont ordonnées par un processeur dont nous simulons et contrôlons l’activité. En effet, chaque tâche physique ou thread POSIX peut demander à avoir accès au CPU. En fonction de sa priorité (selon la politique RM), le processeur lui attribuera ou non une unité de temps de temps et simulera une unité de temps d’exécution.
La fonction computeDuringTimeSpan
fait ainsi exécuter une tâche
pendant une certaine quantité de temps lorsque celle-ci devient
prioritaire pour le processeur. Plusieurs fonctions décrites dans le
fichier tasks.h
permettent de controler l’activité de ces
tâches. Celles que vous devez utiliser dans ce travail sont indiquées au
début du fichier.
Pour découvrir l’architecture de l’application, nous allons terminer
l’implantation des tâches périodiques et plus particulièrement de leur
procédure principale runPeriodicTask
dans periodictasks.c
.
Il s’agit faire en sorte que la tâche logique s’exécute pendant son
budget et attendre sa prochaine activation.
Vous pouvez évaluer votre travail en utilisant les scenarii de 1 à 3.
Le premier est très simple, le deuxième échoue et le troisième
correspond au deuxième en termes d’utilisation, mais dont les temps de
calcul et les périodes ont été modifiées. Ces exemples sont présents
dans le cours. Des sorties de réference sont fournies dans les fichiers
scenario-<N>.ref
où N varie entre 1 et 7.
Pour comparer votre exécution à celle de référence :
make ./runapsvr 1 > scenario-1.log diff scenario-1.log scenario-1.ref
Attention : il peut y avoir des différences d’affichage.
49d48 < p1 40 activated 50a50 > p1 40 activated
Comme certains événements arrivent simultanément, il arrive que ceux-ci soient signalés dans un ordre aléatoire. Typiquement, lors que p1 et p2 ont des activations simultanées.
De même que pour les tâches logiques périodiques lorsqu’un scenario est
selectionné, toutes les tâches logiques apériodiques associées à ce
scenario sont ajoutées au tableau aperiodicTaskTable
.
A la différence des tâches logiques périodiques, les tâches logiques
apériodiques ne sont pas associées à des tâches physiques ou threads
POSIX. Elles sont retranscrites en une structure Event
décrite
dans le fichier events.h
puis seront stockées dans un tableau
eventTable
.
Lorsque le programme démarre, les tâches apériodiques sont donc
retranscrites en événements de type Event
. Ces événements seront
pris en compte par les serveurs de tâches apériodiques. Plusieurs autres
événements viendront se rajouter à ce tableau et notamment les
événements de gestion de budget de temps des serveurs de tâches
apériodiques (comme le réapprovisionnement des budgets de temps).
Pour comprendre la différence entre événements et tâches apériodiques,
nous allons dérouler une exécution. Lorsqu’une tâche apériodique doit
être activée à la date T=7s et nécessite un temps de calcul C=3s, un
événement de type CONSUME
(de temps de calcul) équivalent est
créé.
A la date T=7s, cet événement peut être traité et supposons que seulement 2s de calcul puissent lui être consacrées. Nous aurons donc besoin de générer un nouvel événement d’activation T=7s mais de temps de calcul C=1s puisqu’un calcul de 2s a déjà été consacré à la tâche apériodique.
Un serveur de tâches apériodiques qui voudrait produire un
réapprovisionnement de son budget à la date T=8s pour une valeur de C=2s
peut générer un événement de type PRODUCE
en indiquant une date
d’activation T=8s et un temps de calcul C=2s.
Les fonctions removeEvent
et appendEvent
permettent
d’extraire et d’ajouter des évenements dans la table en gardant celle-ci
triée par ordre d’activation. D’autres fonctions servent à gèrer cette
table.
La mise en oeuvre du serveur en tâche de fond s’effectue en complètant
la procédure principale runBackgroundServer
qui se trouve dans le
fichier backgroundserver.c
.
Nous rappelons que le serveur en tâche de fond ne gère pas de budget de calcul.
Vous devez complèter ce fichier pour mettre en oeuvre le principe de server en tâche de fond.
Les modifications à apporter sont les suivantes :
tasks.h
à cet effet.
computation
de
l’événement e
.
computeDuringTimeSpan
qui permet
d’effectuer des calculs effectifs pendant un temps donné en paramètre de
la fonction.
Vous pourrez évaluer vos modifications en déroulant le scenario 4 et en
vérifiant à l’aide du fichier scenario-4.ref
.
make ./runapsvr 4
La mise en oeuvre du serveur en scrutation s’effectue en complètant la
procédure principale runPollingServer
qui se trouve dans le
fichier pollingserver.c
.
Vous devez complèter ce fichier pour mettre en oeuvre le principe de server en scrutation.
Nous rappelons que le serveur en scrutation gère un budget de calcul. En
début de période, il traite les événements présents dans la limite de
son budget. S’il lui reste du budget une fois les événements présents
traités, il renonce à celui-ci. Il sera renouvellé au moment de sa
période au travers d’un événement PRODUCE
.
Les modifications à apporter sont les suivantes :
Vous pourrez évaluer vos modifications en déroulant le scenario 5 et en
vérifiant à l’aide du fichier scenario-5.ref
.
make ./runapsvr 5
La mise en oeuvre du serveur différé s’effectue en complètant la
procédure principale runDeferredServer
qui se trouve dans le
fichier deferredserver.c
.
Vous devez complèter ce fichier pour mettre en oeuvre le principe de server différé.
Nous rappelons que le serveur différé gère un budget de calcul. En début de période, il traite les événements présents dans la limite de son budget. S’il lui reste du budget une fois les événements présents traités, à la différence du serveur en scrutation, il ne renonce pas à ce budget. L’intégralité sera renouvellée au moment de sa période.
Les modifications à apporter sont les suivantes :
Vous pourrez évaluer vos modifications en déroulant le scenario 6 et en
vérifiant à l’aide du fichier scenario-6.ref
.
make ./runapsvr 6
La mise en oeuvre du serveur sporadique s’effectue en complètant la
procédure principale runSporadicServer
qui se trouve dans le
fichier sporadicserver.c
.
Vous devez complèter ce fichier pour mettre en oeuvre le principe de server sporadique.
Nous rappelons que le serveur sporadique gère un budget de calcul. Il traite les événements présents dans la limite de son budget. A la différence des serveurs précédents, il ne programme pas son réapprovisionnement de budget en début de période. Lorsqu’il utilise une certaine quantité de calcul, il programme un réapprovisionnement de son budget à la hauteur de ce qu’il utilise à la date de consommation plus sa période.
Les modifications à apporter sont les suivantes :
Vous pourrez évaluer vos modifications en déroulant le scenario 7 et en
vérifiant à l’aide du fichier scenario-7.ref
.
make ./runapsvr 7