Pages professionnelle – Thomas Robert

Maître de Conférence

Correction Tolérance aux Fautes I

I Exceptions en C , signaux et traitement de signaux

Pour traiter cet exercice il y a deux solution :

  • Soit l’on réagit à l’erreur dans l’état d’exécution du processeur (i.e. au signal SIGSEGV)
  • Soit l’on traite la faute (i.e. le paramètre manquant) en testant argc pour vérifier qu’il y a bien un argument passé à la ligne de commande.

Nous allons détailler la solution utilisant le traitement du signal qui de notre point de vue correspond au traitement d’erreur.

NB: l’énoncé contenait un statement goto utilisable uniquement pour la deuxième solution.

Code solution commenté (les solutions sont aussi dans cette archive ) :

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <stdlib.h>
  4. void catchSEGV (int);
  5. int main (int argc, char* argv[]){
  6. signal(SIGSEGV,catchSEGV);
  7. printf(« Premier caractère du premier arguments %c », argv[1][0]);
  8. // 0 signifie une exécution correcte
  9. return 0;
  10. }
  11. void catchSEGV(int signum){
  12. exit(-1);
  13. }

Analysons tout d’abord l’entête : nous avons besoins d’ajouter signal.h pour avoir la signature de signal() et la macro SIGSEGV. Nous ajoutons stdlib.h pour la signature de la fonction exit qui permet de terminer un programme depuis une fonction autre que main (possède d’autres propriétés mais nous ne les utilisons pas ici).

Le handler de signal doit être configurer avant d’exécuter le code susceptible d’engendrer l’erreur : donc avant la ligne 7. Dans le handler, la seule chose à faire est de contrôler la fin de l’exécution et de faire en sorte que le processus se termine en produisant la valeur -1.

Suite à l’exécution du programme vous pouvez contrôler la valeur produite en exécutant echo $?

Cette commande doit afficher 0 si vous exécutez ce programme avec un argument, et 255 (équivalent à -1) sinon.

La solution alternative est fournie par soucis d’exhaustivité. On notera que dans cette deuxième version nous avons utilisé goto (même si il n’y avait pas d’intérêt majeur ici). Un intérêt usuel de l’usage de goto est de pouvoir altérer le contrôle d’exécution sans avoir à dupliquer le code. C’est parfois pratique pour faire du forward recovery.

#include <stdio.h>

int main (int argc, char* argv[]){
if (argc<2){goto erreur1;}
printf(« Premier caractère du premier arguments %c », argv[1][0]);
// 0 signifie une exécution correcte
return 0;

erreur1:
return -1;
}

II Fautes transientes, débuggage vs tolérance

L’exercice est organisé en 3 étapes :

1) une étape d’observation

2) une étape d’élimination de faute

3) une étape concerné par la tolérance aux fautes

Etape 1)

Si vous n’activez pas les macros de temporisation la condition de compétition sur les donnée (race condition) ne devrait pas se manifester et vous devriez voir un affichage similaire à ceci.

S-> Demarrage de la simulation…

X : 0.862412; Y : 0.506206; R : 1.000000;
X : 0.761466; Y : 0.648205; R : 1.000000;
X : 0.637406; Y : 0.770528; R : 1.000000;
X : 0.493999; Y : 0.869463; R : 1.000000;
X : 0.335596; Y : 0.942006; R : 1.000000;
X : 0.167008; Y : 0.985956; R : 1.000000;
^CS-> Salut !

***********
Dernier etat connu (X,Y):(-0.006651,0.999978)

En utilisant la commande make VT1, vous produisez sV1tempo qui donne un résultat différent simplement à cause d’un entrelacement différent des accès à x et y :

 

S-> Demarrage de la simulation…

X : -0.768645; Y : 0.000000; R : 0.590815;
X : -0.645957; Y : -0.763374; R : 1.000000;
X : -0.503661; Y : -0.863901; R : 1.000000;
X : -0.346077; Y : -0.938206; R : 1.000000;
X : 0.495869; Y : -0.868397; R : 1.000000;
X : 0.639063; Y : -0.769154; R : 1.173553;
X : 0.762860; Y : -0.646564; R : 1.163678;
X : 0.937930; Y : -0.346824; R : 1.000000;
X : 0.983890; Y : -0.178772; R : 1.000000;
^CS-> Salut !

***********
Dernier etat connu (X,Y):(0.983890,-0.178772)

On notera l’état erroné où la distance au point (0,0) n’est pas égale à 1.

Etape 2

Pour corriger ce problème il faut procéder en deux étapes :

introduire un sémaphore initialisé à 1, puis positionner dans les fonctions afficheur et simu_systeme les appels à sem_wait et sem_post.

On notera que nous avons encadré au plus juste les accès à x,y au pus juste afin de ne pas altérer la logique de la simulation qui repose sur la répétition du code d’affichage et de mise à jour de position de manière périodique ( dernière ligne de la boucle dans chaque fonction).

La solution peut être téléchargée ici. On notera en particulier le contenu des lignes 29, 36, 45,53,64. (Ce sont les lignes contenant les appels au service de sémaphore).

Etape 3

Pour cette dernière phase, il faut tout d’abord constaté que si vous exécuter en parallèle votre programme et « bash benchit.sh » avec pour paramètre le nom de l’exécutable que que vous avez créé à l’étape 2, R peut toujours être différent de 1…

Pour corriger ce problème il faut récupérer le code de retour de sem_wait et analyser sa valeur, si il vaut -1 sem_wait n’a pas modifier le sémaphore et a priori ne bloque plus le processus correctement. Cependant, cela ne veut pas dire que l’on puisse régler le problème systématiquement par rééxécution de sem_wait. Ce n’est vrai que dans le cas où la fonction a défailli dans le cas EINTR. Pour analyser l’origine de la défaillance, vous devez inclure le header errno.h qui permet de consulter une variable globale : errno qui mémorise le code d’erreur de la dernière fonction défaillance exécutée.

L’ensemble des fichiers de solution aux étapes 2 et 3 se trouvent ici

Commentaires Clos.