#1 Le 21/06/2017, à 10:04
- DonutMan75
[RESOLU] Sauts non locaux en langage C
Bonjour à tous,
j'essaie de comprendre le fonctionnement des fonctions sigsetjmp() et siglongjmp() en C.
Pour les curieux, ce code est adapté de l'exemple donnée par C. Blaess dans le chapitre dédié aux signaux de son livre "Programmation système en C sous Linux".
Description du programme :
Le programme demande à l'utilisateur d'entrer deux entiers a et b et affiche le résultat de la division entière r = a/b.
En cas de calcul incorrect (typiquement lorsque b=0), le programme lève un signal SIGFPE qui sera capturé par un gestionnaire dont le but est de restaurer un environnement sain et de continuer l'exécution du programme.
Les signaux sont paramétrés à l'aide de sigaction.
La sauvegarde et la restauration du contexte de la pile sont gérés par sigsetjmpt() et siglongjmp()
Description du problème :
Le code s'exécute correctement, le signal_handler gestionnaire() est correctement appelé...
Néanmoins, je comprends pas vraiment ce qui est restauré lors de l'appel de siglongjmp().
Voir le détail ci-dessous.
EDIT
Solution du problème :
Comme l'a cité eiger :
All accessible objects have values, and all other components of the abstract machine have state (for example, floating-point status flags and open files), as of the time longjmp() was called, except that the values of objects of automatic storage duration are unspecified if they meet all the following conditions ...
Le comportement est donc normal
Code source :
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <setjmp.h>
sigjmp_buf contexte;
void gestionnaire(int numero)
{
fprintf(stderr, "- Signal %d [%s] capturé : PREPAREZ-VOUS POUR UN RETOUR EN ARRIERE !\n", numero, sys_siglist[numero]);
siglongjmp(contexte, 1);
fprintf(stderr, "Ce message ne devrait donc jamais s'afficher...\n");
}
int main(void)
{
time_t now; // Nombre de seconde
struct tm *tm; // Structure tm (champs sec, min, heure, jour etc...)
char whenami[128]; // Chaîne littératle (2017-06-21 à 08:00:00)
struct sigaction action;
action.sa_handler = gestionnaire;
sigemptyset(&(action.sa_mask));
action.sa_flags=0;
sigaction(SIGFPE, &action, NULL); // On capture les opérations arithmétiques illicites
now = time(NULL);
tm = localtime(&now);
strftime(whenami, 128, "%F à %H:%M:%S", tm);
int a=0, b=0, r=0;
// On sauvegarde le contexte
int retour = 0;
retour = sigsetjmp(contexte, 1);
while (1)
{
fprintf(stderr, "Nous sommes le %s, retour = %d, a=%d, b=%d, r=%d\n", whenami, retour, a, b, r);
fprintf(stderr, "\tr = a/b : a = ");
fscanf(stdin, "%d", &a);
fprintf(stderr, "\tr = a/b : b = ");
fscanf(stdin, "%d", &b);
r = a/b;
fprintf(stderr, "\ta/b = %d\n", r);
// Mise-à-jour du temps
now = time(NULL);
tm = localtime(&now);
strftime(whenami, 128, "%F à %H:%M:%S", tm);
}
}
Exemple d'éxécution:
$ ./essai
Nous sommes le 2017-06-21 à 10:54:01, retour = 0, a=0, b=0, r=0
r = a/b : a = 10
r = a/b : b = 2
a/b = 5
Nous sommes le 2017-06-21 à 10:54:07, retour = 0, a=10, b=2, r=5
r = a/b : a = 20
r = a/b : b = 2
a/b = 10
Nous sommes le 2017-06-21 à 10:54:10, retour = 0, a=20, b=2, r=10
r = a/b : a = 30
r = a/b : b = 0
- Signal 8 [Floating point exception] capturé : PREPAREZ-VOUS POUR UN RETOUR EN ARRIERE !
Nous sommes le 2017-06-21 à 10:54:10, retour = 1, a=30, b=0, r=10
r = a/b : a = 40
r = a/b : b = 2
a/b = 20
Nous sommes le 2017-06-21 à 10:54:15, retour = 1, a=40, b=2, r=20
Après réception de SIGFPE je m'attendais à une restauration complète de la pile (càd des variables dont la taille mémoire est connue lors de la compilation non ?), à savoir notamment les variables a, b, r et les variables de temps now, tm et whenami.
Je m'attendais donc à avoir :
- Signal 8 [Floating point exception] capturé : PREPAREZ-VOUS POUR UN RETOUR EN ARRIERE !
Nous sommes le 2017-06-21 à 10:54:01, retour = 1, a=0, b=0, r=0 <== VARIABLES DE LA PILE EGALES A LEUR ETAT AU MOMENT DE L'APPEL A SIGSETJMP()
En lieu et place de :
- Signal 8 [Floating point exception] capturé : PREPAREZ-VOUS POUR UN RETOUR EN ARRIERE !
Nous sommes le 2017-06-21 à 10:54:10, retour = 1, a=30, b=0, r=10 <== VARIABLES DE LA PILE EGALES A LEUR ETAT JUSTE AVANT SIGFPE
Est-ce normal ? Le man de siglongjmp() indique pourtant :
The corresponding longjmp() functions restore the environment saved by their most recent respective invocations of the setjmp() function. They then return, so that program execution continues as if the corresponding invocation of the setjmp() call had just returned the value specified by val, instead of 0.
Merci d'avance pour votre retour, je suis un peu perdu ^^
Donut
Dernière modification par DonutMan75 (Le 22/06/2017, à 06:50)
Hors ligne
#2 Le 21/06/2017, à 12:32
- J5012
Re : [RESOLU] Sauts non locaux en langage C
sigsetjmp est appelé dans le programme à chaque invocation de la variable "retour" avant le calcul des valeurs a et b ...
Hors ligne
#3 Le 21/06/2017, à 12:43
- DonutMan75
Re : [RESOLU] Sauts non locaux en langage C
Bonjour J5012,
merci pour ton retour mais je ne comprends toujours pas.
Dans les faits, je constate bien que le contexte qui est restauré c'est celui juste avant l'opération illicite r=a/b. Donc a=30, b=0 et r=10.
Qu'entends-tu exactement par " invocation de la variable "retour" " ?
Quand est-ce que le contexte est effectivement sauvegardé ?
De toute évidence pas au moment de l'appel à sigsetjmp()...
Du coup, peu importe où on place l'appel sigsetjmp() du moment que c'est AVANT la section critique du code ?
Ca me semble peu clair tout ça...
Merci d'avance pour votre aide !
Donut
Hors ligne
#4 Le 21/06/2017, à 12:54
- J5012
Re : [RESOLU] Sauts non locaux en langage C
// On sauvegarde le contexte
int retour = 0;
retour = sigsetjmp(contexte, 1);
fprintf(stderr, "Nous sommes le %s, retour = %d, a=%d, b=%d, r=%d\n", whenami, retour, a, b, r);
l'instruction fprintf invoque "retour"
Hors ligne
#5 Le 21/06/2017, à 13:32
- DonutMan75
Re : [RESOLU] Sauts non locaux en langage C
Bonjour,
mais retour est un int ?
int sigsetjmp(sigjmp_buf env, int savemask);
Je ne comprends pas en quoi l'utiliser dans fprintf() invoque quoi que ce soit...
Désolé je suis un peu lent à la détente (sans doute à cause de la canicule )
Merci pour ton aide en tout cas !
Donut
Dernière modification par DonutMan75 (Le 21/06/2017, à 14:36)
Hors ligne
#6 Le 21/06/2017, à 17:26
- eiger
Re : [RESOLU] Sauts non locaux en langage C
Salut,
Le comportement que tu observes est tout ce qu'il y a de plus normal : c'est le comportement décrit dans la spec.
Les fonctions sigsetjmp et siglongjmp sont des versions POSIX des fonctions C standard setjmp et longjmp, à la différence qu'elles sont capables de restaurer le sigmask.
Voir http://pubs.opengroup.org/onlinepubs/96 … ngjmp.html
Donc on va voir la description de longjmp pour le comportement (soit dans la spec du C, soit dans la spec POSIX): http://pubs.opengroup.org/onlinepubs/96 … ngjmp.html.
Et là on trouve :
All accessible objects have values, and all other components of the abstract machine have state (for example, floating-point status flags and open files), as of the time longjmp() was called, except that the values of objects of automatic storage duration are unspecified if they meet all the following conditions ...
Soit : les variables ont l'état qu'elles avaient au moment de l'appel à longjmp.
Et c'est bien ce qui se passe chez toi.
Autrement dit : tu souhaites peut être autre chose, maisle comportement est conforme à la spec.
Dernière modification par eiger (Le 21/06/2017, à 17:27)
Hors ligne
#7 Le 21/06/2017, à 20:31
- J5012
Re : [RESOLU] Sauts non locaux en langage C
Bonjour,
mais retour est un int ?man sigsetjmp a écrit :int sigsetjmp(sigjmp_buf env, int savemask);
Je ne comprends pas en quoi l'utiliser dans fprintf() invoque quoi que ce soit...
Désolé je suis un peu lent à la détente (sans doute à cause de la canicule )
Merci pour ton aide en tout cas !
Donut
ben revois les regles de programmation ?
→ fprintf invoque la variable "retour" → qui est definie au debut du programme par l'appel à sigsetjmp ... → à chaque nouvelle invocation de "retour" , sigsetjmp est appelé ...
Hors ligne
#8 Le 21/06/2017, à 23:57
- DonutMan75
Re : [RESOLU] Sauts non locaux en langage C
Bonsoir eiger,
merci *énormément* pour cette remarque constructive ! J'avance enfin, je vais explorer ça !!
J'étais resté sur le man mais de toute évidence il n'était pas complet pour comprendre totalement ces fonctions Oo'
Pire, je le trouve limite ambigu quand il écrit :
longjmp()
restores the environment saved by the last call of setjmp(3) with the corresponding env argument.
Quoiqu'il en soit, l'emplacement de l'appel à setjmp() importe donc finalement peu..
Bonne journée à tous,
Donut
Dernière modification par DonutMan75 (Le 22/06/2017, à 06:51)
Hors ligne