Forum Programmation.c Système

Posté par . Licence CC by-sa
Tags :
1
23
oct.
2015

Salut je bricole du C dans mon garage.

Mon but c'est d'empécher l'utilisateur d'un programme de faire un Ctrl + C pendant une tâche critique.

Donc j'ai trouvé la fonction sigprocmask qui permet de manipuler(edit : bloquer) certains signaux du système.

Ca fonctionne bien mais je ne comprend pas pourquoi je récupère la main sur le terminal malgrès que j'ai mis un blockage du signal SIGHUP.

Par contre l'execution n'est pas interrompu et c'est ce que je voulais.

  • # Un bout de code ???

    Posté par . Évalué à 2.

    Tout est dans le titre.

    • [^] # Re: Un bout de code ???

      Posté par . Évalué à 1. Dernière modification le 24/10/15 à 19:13.

      Alors je fais comme cà :

      /* fichier.h : définitions des signaux a traiter */
      /* fichier.h : */
      
              sigset_t sig, oldsig;
              sigemptyset(&sig);
      
      /* on ajoute les signaux qui nous intéresse a bloquer */
      /* (j'en ai mis beaucoup car je suis pas sur de celui que je cherche ) */
      
              sigaddset(&sig, SIGINT);
              sigaddset(&sig, SIG_IGN);
              sigaddset(&sig, SIG_DFL);
              sigaddset(&sig, SIGKILL);
              sigaddset(&sig, SIGTSTP);
              sigaddset(&sig, SIGQUIT);
              sigaddset(&sig, SIGSTOP);
              sigaddset(&sig, SIGHUP);
      
      /* et donc on appel la fonction qui masque les signaux */
              sigprocmask(SIG_BLOCK, &sig, &oldsig);

      dans mon application :

      /* application.c */
      
      /* appel du bloquer à l'endroit où on peu faire une action */
              fonction() {
             #include fichier.h
         [..] 
            sigprocmask(SIG_SETMASK, &oldsig, NULL);
              return();
      }

      Heu c'est peut être plus clair sans les commentaires.

      • [^] # Re: Un bout de code ???

        Posté par . Évalué à 1.

        Je ne peu plus améliorer la mise en page, si un magicien avec pouvoirs passe par là pour me permettre d'arranger ca ca serait sympa .

        • [^] # Re: Un bout de code ???

          Posté par . Évalué à 4.

          Je ne peu plus améliorer la mise en page, si un magicien avec pouvoirs passe par là pour me permettre d'arranger ca ca serait sympa .

          l'ouverture etait bonne ```C
          mais il manquait le saut de ligne AVANT, et la fermeture au bout de ton code avec ```

          j'ai corrigé ca.

      • [^] # Re: Un bout de code ???

        Posté par . Évalué à 3.

        1) il peut-être intéressant de vérifier le retour de sigprocmask (hum…)
        2) " A signal may be blocked, which means that it will not be delivered
        until it is later unblocked. Between the time when it is generated and
        when it is delivered a signal is said to be pending."
        (de man 7 signal) : es-tu sur que ton programme est bien intérompu entre tes 2 appels à sigprocmask ?
        3) pour ignorer un signal tu es du coup obligé d'écrire ton handler…
        À partir de ce moment, je te conseille d'ouvrir un bouquin de programmation unix, car entre les threads, le polling d'évenements, l'éxéuctions de sous-processus, etc, il y a pas mal de subtilités (de la à dire que les programmes qui les gère correctement ne font pas légions, il n'y a qu'un pas…) Les signaux sont souvent considérés comme une moins bonne invention d'UNIX, à mon avis, à juste titre :-)

        • [^] # Re: Un bout de code ???

          Posté par . Évalué à 4.

          By the way, c'est uniquement de SIGINT dont tu as besoin pour intercepter le ctrl-c… Tout spécifier "en espérant que ça marche" démontre simplement que tu n'as pas compris l'utilisation des signaux. Ceux-ci ne sont uniquement qu'un mécanisme de notification asynchrone et préemptif (si tu comprends pourquoi ces deux mots sont importants, alors c'est que tu comprends comment fonctionnent les signaux) du noyau à l'attention des processus. Il va de soit que chaque signal a son utilité spécifique et doit être traité comme il se doit.

        • [^] # Re: Un bout de code ???

          Posté par (page perso) . Évalué à 2.

          pour ignorer un signal tu es du coup obligé d'écrire ton handler

          Non.

          sigpending(3) nous dit clairement :

          If a signal is both blocked and has a disposition of "ignored",
          it is not added to the mask of pending signals when generated.

          Par contre ça se fait avec sigaction, pas sigprocmask.

          pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: Un bout de code ???

        Posté par (page perso) . Évalué à 2.

        SIG_IGN et SIG_DFL ne sont pas des arguments valides pour sigaddset. Je n'arrive pas à reproduire le comportement que tu décris mais si je retire ces deux lignes, j'ai le comportement que tu recherches.

        Cela dit, tu n'a besoin que de bloquer SIGINT pour faire ce que tu veux. D'un autre côté, selon ton but ultime, c'est peut-être insuffisant ou il y a peut-être d'autres moyens plus propres.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

  • # autre signal à traiter ?

    Posté par (page perso) . Évalué à 2.

    Tout est dans le titre

    Système - Réseau - Sécurité Open Source

  • # désactiver ctrl-c

    Posté par (page perso) . Évalué à 2.

    Plutôt que d'ignorer SIGINT, tu pourrais peut-être dire au terminal d'ignorer Ctrl-C pour qu'il ne génère pas le signal. Ça se fait avec tcsetattr(IGNBRK) en gros. Voire termios(3).

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: désactiver ctrl-c

      Posté par . Évalué à 2.

      Bonne idée ça que de l'envoyer dans un domaine encore plus dégeux que la gestion des signaux, à savoir la gestion des tty :-)

      • [^] # Re: désactiver ctrl-c

        Posté par (page perso) . Évalué à 2.

        L'API est peut-être moins joli mais c'est plus difficile de l'utiliser de manière incorrect.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: désactiver ctrl-c

      Posté par . Évalué à 10.

      Le plus simple 'est de retirer du lavier la touhe assoiée dans ontrol+, ou bien la touhe ontrol elle même,
      omme à, plus possible de taper la ombinaison fautive !

      Je teste en e moment même !

  • # exploiter le Ctrl+C plutôt que de le masquer ?

    Posté par . Évalué à 4.

    Personnellement j'utilise plutôt ce code :

    #include <signal.h>
    #define CTRL_C_LIM 3
    int please_exit = 0;
    void exitApp(int sig)
    {
       if (please_exit < CTRL_C_LIM)
       {
          fprintf(stderr,"CTRL+C received");
          please_exit++;
       }
       else
       {
          fprintf(stderr,"CTRL+C received too many times, forcing exit");
          exit();
       }
    }
    
    int main(int argc, char** argv)
    {
        signal(SIGINT, exitApp);
        // programme principal
        // dès que la variable please_exit est non nulle, on quitte proprement le programme
        // par exemple
        while (!please_exit)
        {
            // ...
        }
    }

    Ça permet au programme de balancer des "attend un peu que je finisse" tout en permettant à l'utilisateur de marteler Ctrl+C pour tuer violemment le programme.

    Accessoirement ce bout de code fonctionne aussi bien sous windows que sous Linux.

    • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

      Posté par (page perso) . Évalué à 4.

      fprintf(3) et exit(2) ne peuvent pas être utilisés de manière sûre dans une fonction de gestion de signal. Voire signal(7).

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

        Posté par . Évalué à 1.

        Bien vu, merci de la remarque.

        Je me doutais que le printf risquait de générer des bugs d'affichage dans la console (genre si ça arrive au milieu de l'écriture d'une séquence de commande ansi).
        Par contre il faudra que je remplace mes exit() par _exit().

        L'interception et la tentative de fermeture propre du programme restent par contre valables.

        L'exemple devient donc:

        #include <signal.h>
        
        int please_exit = 0; // si marche pas avec optimiseur, ajouter "volatile", c'est moche mais ça marchera
        void exitApp(int sig)
        {
           please_exit = 1;
        }
        
        int main(int argc, char** argv)
        {
            signal(SIGINT, exitApp);
            // programme principal
            // dès que la variable please_exit est non nulle, on quitte proprement le programme
            // par exemple
            while (!please_exit)
            {
                // ...
            }
        }
        • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

          Posté par (page perso) . Évalué à 3.

          please_exit doit sans doute être de type sig_atomic_t et/ou volatile.

          pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

          • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

            Posté par . Évalué à 1.

            Pour le type sig_atomic_t, il faut pas exagérer. A part sur Pic (et 8051 ?) le type "int" fait 32 bits, donc l'écriture (ce n'est plus un read-modify-write) se fait en 1 cycle (non-interruptible par définition).
            Et vu que la variable ne fait que passer de 0 à 1, il n'y a aucun risque de glitch. Ce n'est pas comme si 2 threads faisaient du read-modify-write dessus.

            • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

              Posté par . Évalué à 1. Dernière modification le 24/10/15 à 17:40.

              Désolé, cela n'a rien à voir avec le nombre de cycles. Le problème vient des optimisations que le compilateur peut faire. Si please_exit est optimisé pour être accèdé via un registre dans main, alors lorsque que le programme va se retrouvé préempté pour exécuter le signal, ben ce registre va se retrouver sauvegardé dans le "contexte" avec les autres registres sauvegardés au niveau du kernel et puis au retour, il sera restauré et, au final, la mauvaise valeur sera utilisée, voir réécrite, par main. Dans une main-loop telle que celle là, c'est sans doutes peu probable que cette optimisation se fasse (quoi qu'il faudrait que le compilateur puisse déterminer que toutes les fonctions appellées ne modifient pas la variable globale, ce qui est loin d'être impenssable) mais sur le fond, Krunch a raison…

            • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

              Posté par . Évalué à 2. Dernière modification le 24/10/15 à 17:53.

              PS: ce n'est pas vrai qu'une écriture se fait en un cycle. Et puis même si c'était le cas, je crois que tu confonds avec un problème d'accès concurrentiel multi-CPU (et cela nécessite toujours un volatile_t pour empêcher le compilo d'aliaser dans un registre). Un signal n'a rien à voir et c'est une opération très coûteuse: cela correspond à un déscheduling puis rescheduling (donc en passant par le kernel), de quoi vider le pipeline du cpu :-)

      • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

        Posté par . Évalué à 1. Dernière modification le 24/10/15 à 17:14.

        Ah je crois que tu te fourvoies sur ce coup par contre ;-) Ce qui est listé dans la page signal ne sont bien évidemment que les appels systèmes "async-signal-safe"… printf est une fonction de la bibliothèque C, qui (aux dernières nouvelles) appelle le syscall write() qui lui est même est "safe".

        Pour répondre à flavien75, le problème n'est pas tellement que les sorties seraient entre-mellées au milieu de séquence d'échapemment (le terminal y survivra, éventuellement faire une restauration de l'état à la sortie du programme), mais bien que le comportement d'un syscall non async-signa-safe est indéfini car, à cause de la nature préemptive du signal, il pourrait être en cours d'éxécution deux fois. Comme dit juste avant, ce n'est pas un problème pour printf.

        Fin bref pour en revenir à des considérations plus générales, c'est à cause de toutes ces subtilités que l'on recommande de faire un handler générique qui va retransmettre le signal dans la boucle d'évènement du programme (le fammeux "select()"). Pour l'ensemble complet des "recommandations" (c.à.d. ce qui fait la différence entre un programme buggé et un programme correcte), je ne peux que vivement conseiller la lecture/compréhension de toutes les pages man associées ou d'un bouquin de programmation système unix :-P

        • [^] # Re: exploiter le Ctrl+C plutôt que de le masquer ?

          Posté par . Évalué à 2.

          Il va effectivement falloir que je plonge dans ces histoires d'async-signal-safe. Ce sera effectivement intéressant de voir si 2 écritures "simultanées" (programme et signal) peuvent se retrouvées entremêlées ou pas.

          Enfin là pour le coup le but était juste de "neutraliser" les Ctrl+C et cette solution (la seconde) fonctionne à tous les coups et sans risque.

          Dans la première le but du exit() était d'épargner à l'utilisateur d'aller faire un kill -9 quand l'application ne répond vraiment plus du tout. Elle n'était déclenchée que dans les versions beta, les versions finales quittant en général en moins d'une seconde (donc avant que l'utilisateur ne perde patience).

          La remarque de Krunch m'a par contre fait réfléchir à l'effet d'un exit() au moment où la boucle principale exécuterait la fonction atexit(). J'ai effectivement des doutes sur la fiabilité du programme dans ce cas. D'où l'idée de remplacer exit() par _exit().

  • # Cool

    Posté par . Évalué à 1.

    merci pour toutes ces réponses, j'ai un peu du mal à implémenter une solution mon code est un peu byzar en tous cas vos explication m'ont beaucoup aidé à comprendre comment on pouvait résoudre.
    Je pense qu'il va falloir que je révise deux trois trucs effectivement mais en tous cas ca me sera utile!

  • # On avance

    Posté par . Évalué à -5.

    Bon j'ai un peu du mal à adapter tous ces éléments de réponse néanmoins le problème est temporairement résolu par :

    printf("Ne faites surtout pas control + C, c'est pas cool.)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.