next up previous
Up: No Title Previous: No Title

Subsections

Contrôle en C

Ruptures de calcul : les signaux

Les signaux en C permettent d'informer un processus qu'un événement particulier s'est produit. Chaque processus peut gérer un traitement particulier pour un signal donné. Le traitement lié à un signal associe à un signal donné une fonction de traitement. Lors du déclenchement de cet événement le programme exécute la fonction associée puis reprend l'exécution du programme là où il l'avait laissée.

Comme il y a des différences importantes entre BSD et SYSTEM V, on consultera le fichier <signal.h> pour connaître les signaux disponibles. On pose un traitement pour un signal de la manière suivante :

#include <signal.h>

signal(NUMERO_DU_SIGNAL, procedure)
La procédure est paramétrée par un entier (correspondant au numéro du signal). Il est nécessaire de réarmer le traitement d'un signal suite au traitement de celui-ci.
1.
Le signal SIGINT indique une interruption (kill -2 ou ^C) du processus. Poser un récupérateur pour le signal SIGINT qui affiche le message "Pas d'interruption possible" en cas d'arrivée de ce signal. Voici le programme principal :
main ()
{ int i;
  int c=0;
  signal(SIGINT,hand_int);
  printf("un programme qui boucle, essayer ^C pour l'arreter\n");
  while (1)
    {i++;
     if ((i % 100000) == 0) {c++;printf("."); fflush(stdout);}
    } 
}
Ecrire la procédure hand_int.
Solution

fichier sigsegv.c


#include <stdio.h>
#include <signal.h>

void hand_int(int sig)
{ printf("Pas d'interruption ^C active\n");
  printf("Taper ^\\ si vous d\'esirez sortir du programme\n");
  signal(SIGINT,hand_int);
}

main ()
{ int i;
  int c=0;
  signal(SIGINT,hand_int);
  printf("un programme qui boucle, essayer ^C pour l'arreter\n");
  while (1)
    {i++;
     if ((i % 100000) == 0) {c++;printf("."); fflush(stdout);}
    } 
}

2.
Le signal SIGSEGV indique une "violation mémoire", un adressage interdit. Soit le programme suivant tiré de "la Communication sous Unix" page 163 :

small

char *p;

void hand()
{
}
main()
{
  signal(SIGSEGV,hand);
  p=(char *)sbrk(512);
  printf("valeur initiale du point de rupture : %u\n",p);
  printf("valeur du point de rupture apres sbrk : %u\n",sbrk(0));
  while(1)
      *p++='a';
}

Ecrire la fonction void hand() qui affiche la valeur de p et le point de rupture.

Solution

fichier sigsegv.c


#include <signal.h>

char *p;

void hand()
{ printf("signal SIGSEGV recu\n");
  printf(" p a comme valeur %u\n",p);
  exit(0);
}
main()
{
  signal(SIGSEGV,hand);
  p=(char *)sbrk(512);
  printf("valeur initiale du point de rupture : %u\n",p);
  printf("valeur du point de rupture apres sbrk : %u\n",sbrk(0));
  while(1)
      *p++='a';
}

3.
Ecrire une fonction qui compte le nombre d'éléments d'une liste d'entiers. Ecrire ensuite un programme qui appelle cette fonction sur une grande liste et qui répond au signal SIGINT en affichant le nombre d'éléments déjà comptés.

Solution

fichier compte_elt.c


#include <signal.h>
#include <stdio.h>

struct cons {int car; struct cons *cdr;};
typedef struct cons *liste_entier;

liste_entier cons(int a, liste_entier l)
{liste_entier r;
 r=(liste_entier)(malloc(sizeof(struct cons)));
 r->car=a;
 r->cdr=l;
 return r;
}

liste_entier intervalle(int a, int b)
{ if (a > b) return NULL;
  else return cons(a,intervalle(a+1,b));
}

int compteur=0;

int compte(liste_entier l)
{ sleep(1);
  if (l == NULL) return 0;
  else 
    {compteur++;
     return 1 + compte(l->cdr);
    }
}

void hand()
{ printf(" deja compt\'es %d\n",compteur);
  signal(SIGINT,hand); 
}

main(int argc, char *argv[])
{int r;
 int x;
 if (argc == 1)
  {fprintf(stderr,"pas d'arguments\n"); exit (1);}
 else
 { signal(SIGINT,hand);
   x = atoi(argv[1]);
   r = compte(intervalle(1,x));
   printf("%d elements\n",r);
 }
}

Rupture de calcul : setjmp/longjmp

Pour certaines applications l'exécution du programme doit être reprise en un point particulier. Pour cela il est nécessaire d'utiliser des étiquettes dynamiques. On mémorise l'état de la pile d'exécution du processus dans une variable de type jmp_buf défini dans <setjmp.h> avec la fonction setjmp. Cette variable constitue l'étiquette. La fonction longjmp permet ensuite de reprendre l'exécution au point prévu en restaurant l'état de la pile. La valeur de retour de setjmp est 0 après son appel ou une valeur non nulle quand ce retour s'effectue par un longjmp (y compris dans le cas où la valeur retournée par longjmp est 0).

int setjmp(jmp_buf env);
void longjmp (jmp_buf env, int val);
1.
Ecrire une fonction récursive qui calcule le produit des éléments d'une liste d'entiers.
Solution

fichier mult_liste_v1.c


#include <stdio.h>

struct cons {int car; struct cons *cdr;};
typedef struct cons *liste_entier;

liste_entier cons(int a, liste_entier l)
{liste_entier r;
 r=(liste_entier)(malloc(sizeof(struct cons)));
 r->car=a;
 r->cdr=l;
 return r;
}

liste_entier append(liste_entier l1, liste_entier l2)
{liste_entier r;
 if (l1 == NULL) return l2;
 else 
  return (cons(l1->car,append(l1->cdr,l2)));
}

liste_entier intervalle(int a, int b)
{ if (a > b) return NULL;
  else return cons(a,intervalle(a+1,b));
}


int compteur=0;

int mult_liste_v1(liste_entier l)
{ compteur++;
 if (l == NULL) return 1;
 else return l->car*mult_liste_v1(l->cdr);
}
 
int mult_liste(liste_entier l)
{ int r;
  compteur=0;
  r=mult_liste_v1(l);
  printf("calcul en %d etapes : ",compteur);
  return r;
}


void print_liste(liste_entier l)
{
  printf("[ "); 
  while (l) 
   {printf("%d ",l->car);
    l=l->cdr;
   }
  printf("]\n");
}


main(int argc, char *argv[])
{int r;
 int x;
 if (argc == 1)
  {fprintf(stderr,"pas d'arguments\n"); exit (1);}
 else
 { x = atoi(argv[1]);
   r = mult_liste(intervalle(1,x));
   printf("fact %d = %d\n",x,r);
   r = mult_liste(append(intervalle(1,x),cons(0,intervalle(1,x))));
   printf("produit de [1..%d]@[0..%d]=%d\n",x,x,r);
 }
}

2.
Modifier cette fonction pour revenir directement si elle rencontre un zéro dans cette liste.

Solution

fichier mult_liste_v2.c


#include <setjmp.h>
#include <stdio.h>

struct cons {int car; struct cons *cdr;};
typedef struct cons *liste_entier;

liste_entier cons(int a, liste_entier l)
{liste_entier r;
 r=(liste_entier)(malloc(sizeof(struct cons)));
 r->car=a;
 r->cdr=l;
 return r;
}

liste_entier append(liste_entier l1, liste_entier l2)
{liste_entier r;
 if (l1 == NULL) return l2;
 else 
  return (cons(l1->car,append(l1->cdr,l2)));
}
liste_entier intervalle(int a, int b)
{ if (a > b) return NULL;
  else return cons(a,intervalle(a+1,b));
}


int compteur=0;

int mult_liste_v2(jmp_buf env,liste_entier l)
{ compteur++;
 if (l == NULL) return 1;
 else { if (l->car == 0) longjmp(env,1);
        else return l->car*mult_liste_v2(env,l->cdr);
      }
}
int mult_liste(liste_entier l)
{ int r;
  jmp_buf env;
  compteur=0;
  switch(setjmp(env)){
    case 0: { r=mult_liste_v2(env,l); 
              printf("calcul en %d etapes : ",compteur);
              return r;
            }
    case 1: { printf("un zero rencontr\'e \`a l'\'etape %d : ",compteur);
            return 0;
          }
    default: {fprintf(stderr,"cas incroyable\n");exit(0);}
  }
}


void print_liste(liste_entier l)
{
  printf("[ "); 
  while (l) 
   {printf("%d ",l->car);
    l=l->cdr;
   }
  printf("]\n");
}

main(int argc, char *argv[])
{int r;
 int x;
 if (argc == 1)
  {fprintf(stderr,"pas d'arguments\n"); exit (1);}
 else
 { x = atoi(argv[1]);
   r = mult_liste(intervalle(1,x));
   printf("fact %d = %d\n",x,r);
   r = mult_liste(append(intervalle(1,x),cons(0,intervalle(1,x))));
   printf("produit de [1..%d]@[0..%d]=%d\n",x,x,r);
 }
}

Implantation d'exceptions

Le mécanisme d'étiquettes dynamiques permet de construire un mécanisme d'exceptions (comme en ML ou ADA). On définit la syntaxe suivante pour intégrer des exceptions en C.

include "exception.h"

exception e;

Test()
{
  TRY
    Body();
  EXCEPT(e)
    Handler();
  ENDTRY
}
avec le déclenchement d'une exception par :
RAISE(e,v)
e est une exception et v un entier. TRY, EXCEPT, ENDTRY, RAISE seront des macros C, Body et Handler le programme utilisateur. Lors du déclenchement d'une exception, l'exécution du programme va au dernier récupérateur d'exceptions rencontré (TRY ... ENDTRY). Si l'exception déclenchée correspond à un des EXCEPT(e) le code de son Handler est alors exécuté, et le programme continue après le ENDTRY. Si l'exception n'est pas récupérée elle est de nouveau déclenchée pour atteindre le récupérateur précédent.

1.
Définir le type exception.
2.
Ecrire les macros TRY, EXCEPT, ENDTRY.
3.
Ecrire la fonction _RaiseException.
4.
Utiliser ce mécanisme pour le produit d'entiers d'une liste.
5.
Ecrire une fonction qui retourne les n derniers éléments d'une liste.

Solution

fichier exception.h


#include <setjmp.h>

typedef  char * exception;

typedef struct _ctx_block {
  jmp_buf env;
  exception exn;
  int val;
  int state;
  int found;
  struct _ctx_block *next;
} context_block;

#define  ES_EvalBody 0
#define  ES_Exception 1

extern exception ANY;
extern context_block *exceptionStack;
extern void _RaiseException();



#define RAISE(e,v) _RaiseException(&/**/e,v)

#define TRY \
  {\
    context_block _cb;\
    int state = ES_EvalBody;\
    _cb.next=exceptionStack;\
    _cb.found=0;\
    exceptionStack=&_cb;\
    if (setjmp(_cb.env) != 0) state=ES_Exception;\
    while(1){\
      if (state == ES_EvalBody){
    
#define EXCEPT(e)\
    if (state == ES_EvalBody) exceptionStack=exceptionStack->next;\
    break;\
    }\
    if (state == ES_Exception) \
      if ((_cb.exn == (exception)&/**/e) || (&/**/e == &ANY)) {\
        int exception_value = _cb.val;\
        _cb.found=1;

#define ENDTRY \
   }\
   if (state == ES_EvalBody) {exceptionStack=exceptionStack->next;break;}\
   else {exceptionStack=exceptionStack->next;\
         if (_cb.found == 0) _RaiseException(_cb.exn,_cb.val); else break;}\
   }\
}

fichier exception.c


#include <stdio.h>
#include "exception.h"

context_block *exceptionStack = NULL;

exception ANY;

void _RaiseException(exception e, int v)
{
  if (exceptionStack == NULL) 
  {fprintf(stderr,"Uncaught exception\n");
   exit(0);
  }
  else {
   exceptionStack->val=v;
   exceptionStack->exn=e;
   longjmp(exceptionStack->env,ES_Exception);
  }
}

fichier mult_liste_v3.c


#include <stdio.h>
#include "exception.h"

exception Found_zero;

struct cons {int car; struct cons *cdr;};
typedef struct cons *liste_entier;

liste_entier cons(int a, liste_entier l)
{liste_entier r;
 r=(liste_entier)(malloc(sizeof(struct cons)));
 r->car=a;
 r->cdr=l;
 return r;
}

liste_entier append(liste_entier l1, liste_entier l2)
{liste_entier r;
 if (l1 == NULL) return l2;
 else 
  return (cons(l1->car,append(l1->cdr,l2)));
}

liste_entier intervalle(int a, int b)
{ if (a > b) return NULL;
  else return cons(a,intervalle(a+1,b));
}


int compteur=0;

int mult_liste_v3(liste_entier l)
{ compteur++;
 if (l == NULL) return 1;
 else { if (l->car == 0) RAISE(Found_zero,1);
        else return l->car*mult_liste_v3(l->cdr);
      }
}
 
int mult_liste(liste_entier l)
{ int r;
  compteur=0;
  TRY
    { printf("calcul en %d etapes : ",compteur);
      r=mult_liste_v3(l);
    }
  EXCEPT(Found_zero)
    { printf("un zero rencontr\'e \`a l'\'etape %d : ",compteur);
      r=0;
    }
  ENDTRY
    return r;
}


void print_liste(liste_entier l)
{
  printf("[ "); 
  while (l) 
   {printf("%d ",l->car);
    l=l->cdr;
   }
  printf("]\n");
}

main(int argc, char *argv[])
{int r;
 int x;
 if (argc == 1)
  {fprintf(stderr,"pas d'arguments\n"); exit (1);}
 else
 { x = atoi(argv[1]);
   r = mult_liste(intervalle(1,x));
   printf("fact %d = %d\n",x,r);
   r = mult_liste(append(intervalle(1,x),cons(0,intervalle(1,x))));
   printf("produit de [1..%d]@[0..%d]=%d\n",x,x,r);
 }
}

fichier lastn.c


#include <stdio.h>
#include "exception.h"


struct cons {int car; struct cons *cdr;};
typedef struct cons *liste_entier;

liste_entier cons(int a, liste_entier l)
{liste_entier r;
 r=(liste_entier)(malloc(sizeof(struct cons)));
 r->car=a;
 r->cdr=l;
 return r;
}
liste_entier append(liste_entier l1,liste_entier l2)
{liste_entier r;
 if (l1 == NULL) return l2;
 else 
  return (cons(l1->car,append(l1->cdr,l2)));
}

liste_entier intervalle(int a, int b)
{ if (a > b) return NULL;
  else return cons(a,intervalle(a+1,b));
}

void print_liste(liste_entier l)
{
  printf("[ "); 
  while (l) 
   {printf("%d ",l->car);
    l=l->cdr;
   }
  printf("]\n");
}

exception Found;
exception Not_found;

int lastn_aux1(liste_entier l, int n)
{
  if (l == NULL) return 0;
  else 
  { int r;
    r = lastn_aux1(l->cdr,n);
    if (r == n) RAISE (Found,l->cdr);
    else return (r+1);
  }
}

liste_entier lastn_aux2(liste_entier l, int n)
{ 
  if (lastn_aux1(l,n) == n) RAISE(Found,l);
  else RAISE (Not_found,l);
}

liste_entier lastn(liste_entier l, int n)
{ liste_entier r;
  TRY
    r=lastn_aux2(l,n);
  EXCEPT(Found)
    printf("Exception Found recuperee\n");
    r=(liste_entier)exception_value;
  EXCEPT(Not_found)
    printf("Exception Not_found recuperee\n");
    printf("liste trop courte \n");
    r=(liste_entier)exception_value;
  ENDTRY
  return r;
}

main(int argc, char *argv[])
{int n;
 int x;
 liste_entier l,l2;

 if (argc == 2)
  {fprintf(stderr,"pas d'arguments\n"); exit (1);}
 else
 { x = atoi(argv[1]);
   n = atoi(argv[2]);
   l = intervalle(1,x);
   print_liste(l);
   printf("les %d derniers elements de ");
   print_liste(l);
   printf(" = ");
   l2=lastn(l,n);
   print_liste(l2);
 }
}

Utilisation des programmes décrits

On trouvera sur la machine maya.cicrp.jussieu.fr les programmes suivants :

dans le catalogue users/p6ipens/chaillou/enseignements/97-98/programmation/TD3. N'hésitez pas à les compiler (mettre l'option -traditional avec gcc pour faire passer le préprocesseur correctement), les tester, les lire et les modifier!!!

Bibliographie


next up previous
Up: No Title Previous: No Title
Emmanuel CHAILLOUX
11/6/1997