Le but de ce TP est d'appeler une fonction à interval régulier qui analysera elle-même son retard de déclenchement. Vous avez trois séances de 2 heure 30 pour faire ce TP.
De nombreux cas seront à tester. Pour simplifer la programmation, une exécution du programme de test permettra de tester un cas unique. Vous ne devez faire qu'un seul programme de test.
Un script shell (python, ...) lancera le programme de test autant de fois que nécessaire pour tester tous les cas.
|
==> |
|
==> |
|
Elle note l'instant de son premier appel : t0. Elle considère avoir un retard nul lors de son premier appel.
Soit ti le moment du ième appel. Soit dt la période. Le retard de déclenchement est égal à ti - (t0 + i * dt)
Les statistiques sont stockées dans un fichier à la fin de l'exécution sous une forme qui permette une réutilisation facile.
Ces statistiques peuvent être affichées à chaque appel de la fonction pour voir interactivement l'évolution des retards en fonction de ce que fait l'utilisateur.
Les statistiques recueillies donnent pour chaque tranche de retard/avance possible le nombre de déclenchements qui ont eu lieu avec ce retard/avance. Un affichage agréable visuellement pourrait être :
appels en avance exacte appels en retard 6 5 4 3 2 1 0 1 2 3 4 5 6 00000 00000 00000 00000 00000 00000 00033 00065 00000 00001 00000 00000 00001
L'échelle horizontale est en log10. Le nombre indiqué est le nombre d'appels qui ont démarré avec un retard dans la tranche. Les tranches de retard/avance :
Numéro | Intervalle en micro secondes |
---|---|
0 | 0 |
1 | [ 100, 101 [ |
2 | [ 101, 102 [ |
3 | [ 102, 103 [ |
... | ... |
Il est facile de ne mettre que la dernière ligne à jour en terminant son affichage par "\r" au lieu de "\n". Dans ce cas le curseur revient à gauche de la ligne sans descendre d'une ligne. N'oubliez pas de faire un fflush pour vider le tampon de printf.
#include <stdio.h> /* printf() */ #include <unistd.h> /* pause() */ #include <time.h> /* clock_gettime(), timer_settime() */ #include <signal.h> /* signal(), SIGALRM */ |
int clock_gettime(clockid_t clk_id, struct timespec *); struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; |
Il faut ajouter -lrt à l'édition des liens.
int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid); int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec * old_value); struct itimerspec { struct timespec it_interval; /* Timer interval */ struct timespec it_value; /* Initial expiration */ }; |
timer_settime modifie la valeur que vous lui passez, pour voir la valeur qu'il utilise réellement appelez-le une deuxième fois et affichez old_value
sevp permet de définir la donnée à passer
lors de l'envoi du signal.
On a donc pas besoin de variable globale.
/* Détourner le signal vers votre fonction */ struct sigaction sa ; memset(&sa, '\0', sizeof(sa)) ; sa.sa_sigaction = votre_fonction ; sa.sa_flags = SA_SIGINFO ; sigaction(SIGALRM, &sa, NULL) ; /* Suspendre le processus */ pause() ; |
#include <pthread.h> /* Changer la priorité d'une thread en route */ int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param); /* Soi-même */ pthread_t pthread_self(); |
Il faut ajouter -lpthread à l'édition des liens.
Vous allez tester des méthodes d'activation de fonctions à intervalle régulier dans différentes conditions. Il faudra tester tous les cas possibles. Les tests seront tous lancés avec une durée prédéterminée et s'arrêteront une fois celle-ci atteinte.
Ce temps ne devra par être trop grand afin que la machine ne soit pas bloquée indéfiniment lorsqu'il tourne à forte priorité.
Vous devez implémenter 2 méthodes d'activation à intervalle régulier. Ces méthodes doivent déclencher la fonction quand on vient de franchir un multiple de l'intervalle de temps. Si le programme démarre au temps t, la fonction doit être appelée en t, t+dt, t+2dt, t+3dt, ...
Vous essayerez les 2 méthodes en les lançant à partir de :
Le script de lancement lancera votre programme autant de fois que nécessaire dans les environnements suivants :
ATTENTION : quand vous faites un 'tar', les accès disques en écriture n'ont pas lieu au moment où le 'tar' est fait mais quand le système décide d'écrire sur le disque. Ceci peut avoir lieu plusieurs dizaines secondes après la fin de la commande 'tar'. Il n'y a aucun moyen simple d'annuler ces écritures. À vous d'être astucieux pour l'exécution des tests.
Vous essayerez le programme avec comme valeur de dt en micro secondes : 100, 1000, 10000, 100000
Le compte rendu est à rendre 2 semaines après le dernier TP fait sur ce sujet. Vous devez m'envoyer un fichier PDF par mail (thierry.excoffier@univ-lyon1.fr), avec demande d'accusé de réception automatique.
Le compte rendu doit faire moins de 6 pages (hors listing) et il doit contenir :
Si vous faites tous les tests vous avez 2*4*9*4*13=3744 valeurs à présenter et expliquer. Le but ultime est de présenter ces 3744 valeurs sur un seul graphique trivialement compréhensible mettant en valeur tous les phénomènes que vous voulez expliquer. Si vous n'y arrivez pas, vous pouvez faire plusieurs graphiques mais en nombre très restreint.
La note tiendra compte notamment :
Petite remarque : si vous prenez 100 secondes par test, l'acquisition de toutes les valeurs prendra 8 heures. Vous pouvez laisser tourner le programme en quittant le TP et en stockant les résultats dans un répertoire avec peu de risque d'effacement (pas /tmp par exemple). Vous pouvez aussi faire l'acquisition chez vous. Je vous conseille de faire 1 fichier par test pour ne pas perdre de données en cas de plantage mais aussi permettre de suspendre et recommencer facilement les tests.
Attention, le nombre de coeurs, le multithreading et la version du kernel changent complètement les résultats...
Attention : si vous faites vos tests sur un ordinateur portable il faut désactiver TOUTES les méthodes de sauvegarde de courant, les économiseurs d'écrans, etc.