IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Linux Discussion :

Problème avec alarm()


Sujet :

Linux

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 125
    Points : 41
    Points
    41
    Par défaut Problème avec alarm()
    Bonjour, dans le cadre d'un TP je dois coder un programme en C sous linux demander à l'utilisateur d'entrer son âge, il a 10 secondes pour cela, si au bout de 10 secondes il n'a toujours pas entré son âge, l'alarme est déclenchée et réceptionnée, et on redemande d'entrer son âge (avec un longjmp). L'utilisateur a trois essais pour cela. Le problème est que si je ne rentre jamais l'âge, une seule alarme sera déclenchée au lieu de 3! Il y'a un bug mais je ne le trouve pas. Si vous pouviez m'y aider.... Merci !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
     
    #include <unistd.h>
    #include <setjmp.h>
     
    #include <sys/types.h>
     
     
    // Pointeur de fonction
    typedef void (*PtrFnSignal)(int);
     
     
    // Traitement timeout
    void traiterAlarm(int);
     
     
    jmp_buf env;
     
    int compteurEssais = 0;
     
    int main(int argc, char *argv[])
    {
    	int age = 0;
     
    	PtrFnSignal ancienSignal;
     
    	// Remplacement du traitement par défaut
    	ancienSignal = signal(SIGALRM, traiterAlarm);
     
    	if(ancienSignal == SIG_ERR) {
     
    		// Erreur signal()
    		perror("Erreur avec signal()\n");
     
    		exit(1);
    	}
     
    	/*else if(ancienSignal == SIG_HOLD){
     
    		// Signal masqué
    		perror("Erreur avec signal()\n");
     
    		exit(1);
    	}*/
     
    	// Point de saut
    	setjmp(env);
     
    	printf("Entrez votre age: ");
     
    	// Lancer le timeout
    	alarm(10);
     
    	scanf("%d", &age);
     
    	return 0;
    }
     
    void traiterAlarm(int numSig)
    {
    	printf("traiterAlarm!\n");
     
    	if(compteurEssais < 2) {
     
    		compteurEssais++;
     
    		printf("On repose la question\n");
     
    		// On repose la question
    		longjmp(env, 31);
    	}
     
    	else {
     
    		printf("\nTrop tard...\n");
     
    		exit(1);
    	}
    }

  2. #2
    Membre émérite Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Points : 2 280
    Points
    2 280
    Par défaut
    Tu n'as pas le droit d'appeler printf, longjmp ou exit dans un gestionnaire de signaux!!!

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 125
    Points : 41
    Points
    41
    Par défaut
    D'accord, mais sinon ça ne devrait pas modifier le déroulement de l'exécution. Après quelques tests, j'ai l'impression que longjmp() annule les futurs appels à alarm().

    En effet, je fais 3 appels successifs à alarm(), je gère le signal en effectuant un longjmp revenant au setjmp précédant effectué juste avant le premier alarm(). Suite au premier longjmp(), les alarm() suivants ne sont pas traités, pkoi ?

    Merci pour ta réponse.

  4. #4
    Membre émérite Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Points : 2 280
    Points
    2 280
    Par défaut
    Citation Envoyé par touronster Voir le message
    D'accord, mais sinon ça ne devrait pas modifier le déroulement de l'exécution.
    comportement indéterminé, pas la peine d'aller plus loin, corrige déjà ça, poste ton code et on voie.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 125
    Points : 41
    Points
    41
    Par défaut
    Bonjour, oui je suis d'accord, il doit y avoir comportement indéterminé. Mais j'essaye de coller à l'exercice qui dit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Ecrire un programme qui met en oeuvre un « timeout » pour contrôler l’occurrence d’un
    événement dans un délai imparti.
    On prendra comme exemple, l’attente de la réponse d’un utilisateur, saisie sur l’entrée standard,
    à une question affichée sur la sortie standard.
    Si l’utilisateur ne frappe pas sa réponse au clavier dans un délai de 10 secondes, la question
    sera à nouveau posée. Après 2 tentatives infructueuses, le processus affichera un message
    d’erreur et s’arrêtera.
    Le problème sera traité en utilisant un point de reprise et le signal SIGALRM.
    Voilà, merci encore !

  6. #6
    Membre émérite Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Points : 2 280
    Points
    2 280
    Par défaut
    qu'est ce qu'il entend par point de reprise?

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 125
    Points : 41
    Points
    41
    Par défaut
    Merci j'ai trouvé.

    En fait l'interruption était masquée, et comme le service de traitement ne quitte pas normalement, il n'était pas démasqué. Faut alors forcer le "démasquage" avec sigrelse().

    Merci

  8. #8
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par touronster Voir le message
    Merci j'ai trouvé.

    En fait l'interruption était masquée, et comme le service de traitement ne quitte pas normalement, il n'était pas démasqué. Faut alors forcer le "démasquage" avec sigrelse().

    Merci


    SIGALRM se désarme tout seul à chaque fois. Il faut le ré-armer chaque fois qu'l expire...

  9. #9
    Membre confirmé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Points : 646
    Points
    646
    Par défaut
    Il faut faire attention a ne pas faire d'appel bloquant dans les gestionnaires d'interruption.
    Mise à part l'intérêt d'une mise en oeuvre de l'appel longjump()/setjump(), qui n'est pas justifiée ici (d'ailleurs les seuls cas ou je l'ai rencontré ne l'étaient pas non plus) je trouve que tu utilises l'enclume pour écraser le clou.
    le man page indique que ces appels sont utilisés pour la gestion d'erreurs critiques et pour gérer des interruptions. En plus de cela la fonction qui appelle setjmpp() ne doit pas se terminer lorsque tu appels setjmp(), sinon le contexte de pile sauvegardé n'est pas valide, cela fait quand même une belle restriction.

    Faire un select() (POSIX 1) ou un poll() avec un timeout en attente d'entrée sur le stdin (ou autre) aurait été bien plus simple et tu maitriserais mieux le contexte d'exécution.
    De plus, utiliser un gestionnaire de signaux avec alarm() te permet de poursuivre l'exécution de ton programme et d'exécuter une action plus tard (genre controler que tu a fais le boulot dans un laps de temps) or ici tu n'as rien a faire sinon attendre, raison de plus pour utiliser poll()/select().

    Enfin il te faut te rappeler que l'on peut sortir des appels systèmes lents bloquants lorsque le processus reçoit un signal, a la condition que le gestionnaire installé ne demande pas leur redémarrage. Tu peux très bien donc ne rien faire dans ton handler et traiter la sortie de scanf().

    Cependant signal() installe le gestionnaire avec le redémarrage des appels système lents, ce que l'on peut voir avec strace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    rt_sigaction(SIGALRM, {SIG_DFL}, {0x8048490, [ALRM], SA_RESTART}, 8) = 0
    Il te faut donc utiliser sigaction(), voici un exemple de code ci-dessous utilisant un gestionnaire de signal() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
     
    typedef void (*sighandler_t)(int);
     
     
    //! Dummy function to avoid exit on alarm
    void hdle_alarm (int val)
    {
            // do nothing
    }
     
     
     
    int main (void)
    {
            int i = -1 ;
            int j = 3 ;
            sighandler_t hldle_bckp = NULL ;
            struct sigaction act   ;
            struct sigaction oldact  ;
            sigset_t set ;
            int ret = 0 ;
     
            printf("Begin\n");
            /*hldle_bckp = signal (SIGALRM, hdle_alarm) ;
            if (hldle_bckp == SIG_ERR)
            {
                    printf("error handler");
                    return -1 ;
     
            }*/
     
            // Installer notre gestionnaire
            sigfillset (&set) ;
            act.sa_handler = hdle_alarm ;
            act.sa_mask = set ; 
            act.sa_flags = 0 ; // L'interet ici par rapport a signal est de justement est de ne pas redemarrer les appels systemes lents avec le flags SA_RESTART qui est fournit lors de signal()
     
            if ( sigaction (SIGALRM, &act, &oldact) == -1 )
            {
                    printf("error handler");
                    // can disp errno
                    return -1 ;
            }
     
            while ((j--) && (ret<=0) )
            {
                    alarm(1) ;
                    ret = scanf("%d", &i) ;
                    if (ret<=0)
                    {
                            printf("alarm\n") ;
                    }
            }
            printf("End with i=%d\n", i);
            //signal (SIGALRM, hldle_bckp);
            sigaction (SIGALRM, &oldact, NULL);
     
            return 0 ;
    }
    Résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Begin
    alarm
    alarm
    alarm
    End with i=-1
    Mais je te conseille toujours d'utiliser un poll()...

  10. #10
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    125
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 125
    Points : 41
    Points
    41
    Par défaut
    Bonjour, oui je veux bien croire que ce n'est pas le top comme code, et qu'il y'a sûrement mieux à faire. Mais tout ça, c'est imposé par les profs..

    Merci

  11. #11
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    ET ??

    c'est bien ce qu'on t'a donné dans le post précédent...

Discussions similaires

  1. VC++ Direct3D8, problème avec LPD3DXFONT et LPD3DTEXTURE8
    Par Magus (Dave) dans le forum DirectX
    Réponses: 3
    Dernier message: 03/08/2002, 11h10
  2. Problème avec [b]struct[/b]
    Par Bouziane Abderraouf dans le forum CORBA
    Réponses: 2
    Dernier message: 17/07/2002, 10h25
  3. Problème avec le type 'Corba::Any_out'
    Par Steven dans le forum CORBA
    Réponses: 2
    Dernier message: 14/07/2002, 18h48
  4. Problème avec la mémoire virtuelle
    Par Anonymous dans le forum CORBA
    Réponses: 13
    Dernier message: 16/04/2002, 16h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo