Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#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 :

Spec C a écrit :

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 smile


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 :

man siglongjmp a écrit :

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... sad
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 ?

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... hmm

Désolé je suis un peu lent à la détente (sans doute à cause de la canicule tongue )

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 :

Spec C a écrit :

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

DonutMan75 a écrit :

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... hmm

Désolé je suis un peu lent à la détente (sans doute à cause de la canicule tongue )

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 :

man siglongjmp a é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