Java temps reel : Java-RTS
Bertrand Dupouy
SOMMAIRE
Ce TP illustre le développement d'une application temps réel
en utilisant la JVM temps réel de Sun.
-
Temps réel Sun
-
API Java RTS
-
Le support de cours.
Utilisation de la JVM temps réel :
-
Elle est installée sur les machines de la salle C133
et
sur la machine lame5.enst.fr.
-
Il faut être
un utilisateur privilégié pour utiliser cet environnement temps réel
-
les outils de développement s'utilisent
comme suit :
- sur lame5.enst.fr :
sudo /opt/SUNWrtjv/bin/javac
sudo /opt/SUNWrtjv/bin/java
- en salle C133 :
sudo /opt/rtsj/jrts2.1/bin/javac
sudo /opt/rtsj/jrts2.1/bin/java
Le scénario à mettre en place met en jeu
plusieurs tâches :
- Trois tâches temps réel (RealtimeThread ) et périodiques (ordonnancement RMS) :
-
Une horloge qui affiche la date toutes les secondes
sous la forme MM:SS, par exemple :
00:00
00:01
00:02
00:03
-
Une tâche dont la période est 3s, à chaque réactivation elle affiche
son nom et cette date de réactivation,
-
Une tâche dont la période est 4s, à chaque réactivation elle affiche
son nom et cette date de réactivation,
-
La tâche main, thread de type standard. Elle crée les tâches périodiques précédentes puis entre dans une boucle
qui la fait passer dans l'état bloqué pendant
N secondes (où est N est le PPCM des périodes des tâches périodiques)
par appel à sleep().
A chaque réveil elle affiche une trace du type :
--- main :
-
Le handler de gestion des échéances manquées compte ces dernières. Il provoque la fin du programme
si, en les totalisant toutes, plus de dix échéances ont été manquées
On partira du canevas proposé dans le fichier
disponible ici
(ExoRT1.java).
Avant de le compléter, on répondra aux questions suivantes :
Questions préliminaires :
-
Comment doit-on choisir les priorités des deux tâches périodiques et de
l'horloge ? Pourquoi ?
-
Pourquoi les tâches périodiques utilisent-t-elles
une zone mémoire du type LTMemory ?
-
Pourquoi l'espace mémoire (cf. Mem. restante dans la trace ci-dessous)
diminue-t-il à chaque activation de tâche ?
-
Est-il correct de tracer l'exécution avec System.out.println ?
Comment faudrait-il faire ?
A faire :
-
Prendre le code source de ExoRT1.java et compléter les lignes
comportant la mention : A COMPLETER
-
Exécuter le programme ainsi :
- Premier type d'exécution (en utilisant les options par défaut) :
la tâche dont la période est 4 manque des échéances,
- Deuxième type d'exécution :
donner des paramètres d'entrée tels que les tâches se terminent
par épuisement de la mémoire de type LTMemory,
L'exécution du programme doit produire ce type de trace :
$ sudo /opt/SUNWrtjv/bin/java ExoRT1
...
Nom Priorite Activation Periode Cout Echeance
null : 68 nullnull(1000 ms, 0 ns)null
ThRT-1 : 67 (38934 ms, 712828 ns)(3000 ms, 0 ns)(1000 ms, 0 ns)(1500 ms, 0 ns)
ThRT-2 : 66 (38934 ms, 712828 ns)(4000 ms, 0 ns)(1000 ms, 0 ns)(500 ms, 0 ns)
Date d ACTIVATION des threads :(33967 ms, 392220 ns)
Lancement de null : RealtimeThread-0
Lancement de ThRT-1 : RealtimeThread-1
Lancement de ThRT-2 : RealtimeThread-2
Periodes : PPCM = 12000
00:00
00:01
00:02
00:03
Thread[RealtimeThread-2,66,main] : ThRT-2 num : 2date d activation :(38935 ms, 385748 ns)
Thread[RealtimeThread-1,67,main] : ThRT-1 num : 1date d activation :(38935 ms, 385824 ns)
ThRT-2 date RE-activation :(38936 ms, 66948 ns) Mem. restante :28344
ThRT-1 date RE-activation :(38936 ms, 387152 ns) Mem. restante :27096
00:04
00:05
00:06
ThRT-1 date RE-activation :(41934 ms, 815076 ns) Mem. restante :25736
00:07
ThRT-2 date RE-activation :(42934 ms, 802596 ns) Mem. restante :24432
00:08
00:09
ThRT-1 date RE-activation :(44934 ms, 792144 ns) Mem. restante :23128
00:10
00:11
--- main : ThRT-1 present
ThRT-2 date RE-activation :(46934 ms, 787832 ns) Mem. restante :21824
00:12
ThRT-1 date RE-activation :(47934 ms, 789556 ns) Mem. restante :20520
00:13
00:14
On va maintenant mettre en oeuvre un scénario du type producteur/consommateur :
-
Le consommateur est un thread standard qui se réveille de façon aléatoire. A chaque réveil, il
essaie de consommer un élément d'un tampon à N cases.
-
Les producteurs sont des threads périodiques (ordonnancement RMS) de type RealTimeThread. A chaque réveil
un producteur essaie de déposer un élément dans le tampon, si le tampon est pein, il se bloque,
si auncune case n'est disponible, il va rater son échéance.
-
Le handler de gestion des échéances manquées compte ces dernières. Il provoque la fin du programme
si, en les totalisant toutes, plus de dix échéances ont été manquées
-
main se révaille toutes les N secondes (sleep(), où
N est le PPCM des périodes des threads,
-
La taille du tampon et le nombre de producteurs sont donnés sur la ligne de commande.
Les valeurs par défaut sont :
- 2 producteurs de périodes 3 et 4 secondes,
- 24 cases dans le tampon,
A faire :
-
Prendre le code source disponible ici
(ExoRT2.java) et compléter les lignes
comportant la mention : A COMPLETER
-
compléter aussi les lignes marquées A COMPLETER
dans ProdCons.java
on aura aussi besoin de ppcm.java.
-
Exécuter ExoRT2.class en modifiant les paramètres d'entrée, par exemple :
-
4 producteurs et un tampon à 10 cases,
-
quelques producteurs et un tampon à 0 case, ils doivent rester bloqués et manquer
leurs échéances,
-
0 producteur, seule l'horloge doit fonctionner, le consommateur étant bloqué,
L'exécution du programme doit produire ce type de trace :
$ sudo /opt/SUNWrtjv/bin/java ExoRT2 3 4
Nom Priorite Activation Periode Cout Echeance
null : 68 nullnull(1000 ms, 0 ns)null
PRODUCTEUR-1 : 67 (1225188817737 ms, 276108 ns)(3000 ms, 0 ns)(1000 ms, 0 ns)(1500 ms, 0 ns)
PRODUCTEUR-2 : 66 (1225188817737 ms, 276108 ns)(4000 ms, 0 ns)(1000 ms, 0 ns)(1500 ms, 0 ns)
PRODUCTEUR-3 : 65 (1225188817737 ms, 276108 ns)(5000 ms, 0 ns)(1000 ms, 0 ns)(1500 ms, 0 ns)
PPCM des periodes = 60000
Date de lancement des threads : (1225188811767 ms, 674724 ns)
Date d activation : (1225188817737 ms, 276108 ns)
Lancement de null
Lancement de PRODUCTEUR-1
Lancement de PRODUCTEUR-2
Lancement de PRODUCTEUR-3
CONSOMMATEUR-1: DEBUT
00:00
00:01
00:02
00:03
00:04
PRODUCTEUR-1 date d activation :(1225188817738 ms, 29852 ns)
PRODUCTEUR-2 date d activation :(1225188817738 ms, 43704 ns)
PRODUCTEUR-1 depose : 1001
PRODUCTEUR-2 depose : 2001
PRODUCTEUR-3 date d activation :(1225188817738 ms, 62792 ns)
CONSOMMATEUR-1 a pris : 1001 reveil dans : 3061
PRODUCTEUR-3 depose : 3001
00:05
00:06
00:07
PRODUCTEUR-1 depose : 1002
00:08
CONSOMMATEUR-1 a pris : 2001 reveil dans : 5848
PRODUCTEUR-2 depose : 2002
00:09
PRODUCTEUR-3 depose : 3002
00:10
00:11
00:12
Thread[RealtimeServerThread-1,30,] : Echeance depassee pour PRODUCTEUR-1
00:13
CONSOMMATEUR-1 a pris : 3001 reveil dans : 3329
PRODUCTEUR-1 depose : 1003
00:14
Thread[RealtimeServerThread-3,30,] : Echeance depassee pour PRODUCTEUR-2
Pour éviter tout accès mémoire non-prédictible, il faut
(au moins) précompiler les sources et précharger les fichiers .class.
Les étapes à suivre sont les suivantes :
- Génération du byte code,
- Construction la liste de précompilation, option :
-XX:+RTSJBuildCompilationList (->nhrt.precompile)
- Construction la liste de préchargement, options :
-Drtsj.precompile=nhrt.precompile
-XX:+RTSJBuildPreloadList (->itc.preload)
-XX:+RTSJBuildClassInitializationList (->itc.preinit)
- Exécution avec contrôle et suivi de la taille
de la mémoire utilisée
par le garbage collector, options :
-Drtsj.preinit=itc.preinit
-Drtsj.precompile=nhrt.precompile
-Drtsj.preload=itc.preload
-XX:RTGCCriticalReservedBytes=10m -XX:+ITCRT -XX:+PrintGC
On trouvera :