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.
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
.
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);} } }
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.
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'; }
SIGINT
en affichant
le nombre d'éléments déjà comptés.
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); } }
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);
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); } }
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); } }
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)où
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.
_RaiseException
.
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); } }
On trouvera sur la machine maya.cicrp.jussieu.fr
les
programmes suivants :
SIGINT
.
SIGINT
.
setjmp/longjmp
.
_RaiseException
.
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!!!