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

C Discussion :

Attendre un signal particulier


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 48
    Par défaut Attendre un signal particulier
    Bonsoir,

    Je dois créer un programme qui doit recevoir des signaux et en fonction de chaque signal, exécuter une tache particulière et j'aimerais pouvoir faire cela sans passer par un gestionnaire de signal, c'est à dire que je gère directement le signal dans mon prog(du style if(signal1) faire tel action, else if(signal2), ...)
    J'aurais voulu savoir si il existe en c une fonction permettant d'attendre des signaux particulier et de savoir quel signal a été reçu?
    Parce que pause() suspend le processus jusqu'a l'arrivée d'un signal peu importe lequel et la fonction sigsuspend je n'arrive pas trop à la comprendre

    Thank you

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 485
    Par défaut
    Bonjour,

    Tu ne peux pas faire ça sans gestionnaire de signal, pour deux bonnes raisons : la première est que les signaux ne sont PAS un système de message passing contrairement à ce que l'on apprend dans plusieurs écoles. C'est en fait un mécanisme comparable aux IRQ et qui est fait pour faire face aux événements systèmes qui sont extérieurs au processus mais qui nécessitent l'attention de celui-ci, sans quoi le programme lui-même ne pourrait pas continuer. Par exemple, la refermeture du pipe ou du socket dans lequel il est en train d'écrire. C'est également pour cette raison que la majorité des signaux provoquent par défaut la mort du processus s'ils ne sont pas explicitement pris en charge par un gestionnaire. C'est le comportement normal en l'absence de routines dédiées.

    La deuxième, c'est que c'est le système qui envoie un signal à ton processus en le préemptant et que, par conséquent, un signal peut arriver à n'importe quel moment dans ton programme. Il est donc assez difficile de faire un if(signal) pour les prendre en charge à un endroit particulier. Cela signifie aussi que les signaux ne sont pas des événements arrivant en file : à chaque fois qu'un signal est reçu, le gestionnaire est appelé et tous les autres signaux de même numéro sont alors inhibés (et donc perdus) tant que le gestionnaire ne réarme pas manuellement la réception de ces signaux en rappelant la fonction d'installation du gestionnaire.

    Oublie sigsuspend(). Regarde du côté de sigaction(), ou à la limite de signal() si l'autre est trop compliquée.

  3. #3
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 48
    Par défaut
    Merci beaucoup d'avoir pris le temps de me répondre.

    Utiliser signal m'est interdit je dois utiliser les fonctions POSIX

    En fait je sais qu'il faut utiliser un gestionnaire de signal, et je l'ai déjà fait avec sigaction sinon lorsqu'on va recevoir un signal ça va arrêter le processus.

    Mais en fait j'ai du mal à voir comment faire mon projet pour la fac, parce qu'en fait je dois implémenter des automates qui vont s'envoyer des signaux et en fonction du signal reçu dans un état donné ils vont changer d'état et mon soucis c'est que si par exemple j'ai un automate qui est à l’état 0 et que si il reçoit un SIGUSR1 il doit aller à l'état 1 et si il reçoit SIGUSR2(bien entendu j'ai déjà installer un gestionnaire pour ces signaux au préalable ) il doit aller à l'état 2 comment faire pour savoir dans quel état je dois faire la transition quand je suis dans l'automate, même si je fais pause ou sigsuspend je ne saurais jamais quel signal j'aurai reçu??

    De plus, si j'installe un gestionnaire qui change les états de mon automate, si mon automate reçoit un signal alors qu'il est dans un état ou il ne peut pas changer d'état AVEC ce signal alors que dans un autre état il peut changer d'état avec ce signal, il changera quand même d'état, donc je ne vois pas comment faire.

    J'ai pensé à installer un gestionnaire qui va ignorer tous les signaux jusqu'à l'endroit ou j'ai besoin de recevoir un signal et dans lequel j'ignorerais tous les signaux sauf ceux qui m’intéressent mais cela me semble laborieux parce que je vais devoir installer et désinstaller à de nombreuses reprises mon gestionnaire, et je vais devoir en créer un pour chaque état, mais ça pourrait peut être marcher, mais est ce la bonne solution?

    Si tu as une idée de comment je pourrais implémenter tout ça d'une façon plus judicieuse je suis preneur

    En espérant avoir été assez clair dans la présentation de mon projet

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 485
    Par défaut
    Citation Envoyé par yoyozaza Voir le message
    Utiliser signal m'est interdit je dois utiliser les fonctions POSIX
    signal() est une fonction POSIX. Elle fait même partie de la norme C.

    Mais en fait j'ai du mal à voir comment faire mon projet pour la fac, parce qu'en fait je dois implémenter des automates qui vont s'envoyer des signaux et en fonction du signal reçu dans un état donné ils vont changer d'état et mon soucis c'est que si par exemple j'ai un automate qui est à l’état 0 et que si il reçoit un SIGUSR1 il doit aller à l'état 1 et si il reçoit SIGUSR2(bien entendu j'ai déjà installer un gestionnaire pour ces signaux au préalable ) il doit aller à l'état 2 comment faire pour savoir dans quel état je dois faire la transition quand je suis dans l'automate, même si je fais pause ou sigsuspend je ne saurais jamais quel signal j'aurai reçu??
    La réponse est simple : tu ne te sers de tes gestionnaires de signaux que pour mettre le numéro du dernier signal reçu dans une variable, ce qui permet accessoirement à ton gestionnaire de rendre la main très rapidement (ce qui est souhaitable). Ensuite, tu écris ton programme normalement en t'appuyant sur le contenu de cette variable. Tu peux même te permettre de remettre la variable à zéro après l'avoir lue si tu crains de lire plusieurs fois le même message.

    Essaie d'éviter les variables globales si tu le peux même si ce serait le plus facile en l'état. Essaie plutôt de déclarer une structure « contexte » qui contiendrait toutes les informations qu'il faudrait partager et qui ne pourraient être directement transmises, d'instancier cette structure soit en tant que variable locale dans ton main, soit avec un malloc, puis de passer un pointeur vers cette structure en tant que troisième paramètre de sigaction(), et de déposer le numéro du signal dans un champ de cette structure. Ceci permettra à ta fonction principale et à ton gestionnaire de signaux de communiquer sans peine, et pourra facilement évoluer ensuite vers des choses plus sophistiquées sans avoir à réécrire ta fonction d'appel.

    De plus, si j'installe un gestionnaire qui change les états de mon automate, si mon automate reçoit un signal alors qu'il est dans un état ou il ne peut pas changer d'état AVEC ce signal alors que dans un autre état il peut changer d'état avec ce signal, il changera quand même d'état, donc je ne vois pas comment faire.
    Ça, c'est propre aux automates d'états finis eux-mêmes. Les signaux proprement dits n'influent pas là-dessus. Tu as en principe une matrice de transitions, c'est-à-dire un tableau à deux dimensions qui, pour chaque état d'une part et pour chaque transition de l'autre (le numéro du signal) t'indique vers quel état il faut sauter. Tu lis donc ce tableau en fonction du numéro du signal reçu et ça ne pose aucun problème.

    J'ai pensé à installer un gestionnaire qui va ignorer tous les signaux jusqu'à l'endroit ou j'ai besoin de recevoir un signal et dans lequel j'ignorerais tous les signaux sauf ceux qui m’intéressent mais cela me semble laborieux parce que je vais devoir installer et désinstaller à de nombreuses reprises mon gestionnaire, et je vais devoir en créer un pour chaque état, mais ça pourrait peut être marcher, mais est ce la bonne solution?
    Non, en effet, ce serait probablement la pire ! :-)

    C'est intéressant parce que ça te permet de faire une petite rétrospection sur la manière dont tu abordes la chose et pourquoi tu te retrouves ensuite en difficulté. Tu veux partir au plus simple (ce qui est en principe une bonne idée) en essayant de lire les signaux comme des caractères en condition normale, ce qui n'est pas prévu pour, parce que les signaux ne fonctionnent pas tout-à-fait comme tu les imagines. Soit. Mais quand tu t'aperçois que ça ne va pas être faisable en l'état, tu veux mettre en place toute une série de dispositifs pour aller contre le fonctionnement normal du système (ignorer les signaux) et espérer qu'ils soient bien interceptés au moment où on le souhaite. Tout ça pour rester sur ton idée de départ.

    Tu serais surpris de savoir combien ce biais est répandu aussi bien en développement logiciel qu'en gestion de projet en général. Et il est bluffant de voir à quel point il est rare que les intéressés s'en rendent compte. On a le droit de faire le choix de rester sur une idée de départ même si elle est mauvaise, mais à condition d'en être pleinement conscient.

  5. #5
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 48
    Par défaut
    Merci beaucoup de prendre le temps de me répondre

    Essaie plutôt de déclarer une structure « contexte » qui contiendrait toutes les informations qu'il faudrait partager et qui ne pourraient être directement transmises, d'instancier cette structure soit en tant que variable locale dans ton main, soit avec un malloc, puis de passer un pointeur vers cette structure en tant que troisième paramètre de sigaction(), et de déposer le numéro du signal dans un champ de cette structure. Ceci permettra à ta fonction principale et à ton gestionnaire de signaux de communiquer sans peine, et pourra facilement évoluer ensuite vers des choses plus sophistiquées sans avoir à réécrire ta fonction d'appel.
    J'ai pas bien compris ce que tu veux me dire, il y a un moyen de récupérer le signal qui a été reçu et traité dans le gestionnaire ?
    Par contre j'ai pas très bien compris comment on fait ça, c'est quoi exactement cette structure contexte ?

    Parce que si je peux récupérer tout les signaux qui ont été reçu alors je vais pouvoir faire le traitement correspondant dans mon automate et ça me facilitera grandement la tache

    Par contre il y a aussi un autre problème c'est que le prof veut qu'il y ait un fichier qui crée plusieurs processus et qu'on fasse un exec vers d'autres fichiers qui contiennent les autres automates, donc chaque automate se trouve dans des fichiers séparés et ils doivent s'envoyer des signaux entre eux, et je ne sais pas comment faire pour avoir " des variables globales entre fichier " pck je pensais installer le gestionnaire pour tous les signaux qui doivent être modifiés dans le premier fichier celui qui crée tout mais comment faire pour passer les variables globales entre eux?

  6. #6
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Citation Envoyé par yoyozaza Voir le message
    J'ai pas bien compris ce que tu veux me dire, il y a un moyen de récupérer le signal qui a été reçu et traité dans le gestionnaire ?
    Par contre j'ai pas très bien compris comment on fait ça, c'est quoi exactement cette structure contexte ?
    C'est une structure que tu crées et que tu passes en paramètre à sigaction()
    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
    struct MyContext {
       enum { READY, RCV_USR1, .... 
       } LastState;
    } myContext;
     
    static void myHandler(int sig, siginfo_t *si, void *myData) {
        printf("Reçu signal %d \n", sig );
        // en fonction de 'sig' et de 'myContext.LastState' on peut avoir un nouveau 'myContext.LastState'
        ....
    }
     
    int main(int argc, char *argv[]) {
        struct sigaction  sa={};
        myContext.LastState = READY;
        struct sigset_t set;
     
        sigemptyset( &set );
        sigaddset( &set, SIGUSR1 );
        sigaddset( &set, SIGUSR2 );
     
        sa.sa_flags = SA_SIGINFO;
        sigemptyset( &sa.sa_mask );
        sa.sa_sigaction = myHandler;
        sa.sa_mask = set; // empêcher la simultanéité des signaux attendus
        // correctif, il n'est pas possible de passer un contexte local par la norme Posix
        // et une globale est aussi bien qu'un contexte global!
        if ( sigaction( SIGUSR1, &sa, NULL ) == -1)
            handle_error("sigaction");
        if ( sigaction( SIGUSR2, &sa, NULL ) == -1 ) // même fct pour ce signal
            handle_error("sigaction");
        ....
    }
    Citation Envoyé par yoyozaza Voir le message
    Parce que si je peux récupérer tout les signaux qui ont été reçu alors je vais pouvoir faire le traitement correspondant dans mon automate et ça me facilitera grandement la tache
    Oui.
    Citation Envoyé par yoyozaza Voir le message
    Par contre il y a aussi un autre problème c'est que le prof veut qu'il y ait un fichier qui crée plusieurs processus et qu'on fasse un exec vers d'autres fichiers qui contiennent les autres automates, donc chaque automate se trouve dans des fichiers séparés et ils doivent s'envoyer des signaux entre eux, et je ne sais pas comment faire pour avoir " des variables globales entre fichier " pck je pensais installer le gestionnaire pour tous les signaux qui doivent être modifiés dans le premier fichier celui qui crée tout mais comment faire pour passer les variables globales entre eux?
    Il ne faut chercher à avoir des données communes entre les processus, chacun doit gérer son automate avec sa vision du monde (son état sa réaction aux signaux reçus).

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 485
    Par défaut
    Citation Envoyé par yoyozaza Voir le message
    Merci beaucoup de prendre le temps de me répondre

    J'ai pas bien compris ce que tu veux me dire, il y a un moyen de récupérer le signal qui a été reçu et traité dans le gestionnaire ?
    Un signal UNIX (ce qui est presque un abus de langage puisqu'ils sont définis par la norme C) est un événement qui correspond à quelque chose de particulier au niveau du système lui-même, ce qui veut dire que leur sémantique est définie à l'avance : SIGINT, SIGKILL, SIGTERM, SIGPIPE, etc. Mais chacun d'eux porte un numéro bien défini lui aussi (il est officiellement propre au système que tu utilises mais la plupart des unixoÏdes essaient d'utiliser les mêmes, à quelques différences près).

    Ce numéro, il est automatiquement transmis à ton gestionnaire que ce soit avec signal() ou avec sigaction() : c'est tout simplement le paramètre int que l'on oblige à utiliser quand tu déclares ton gestionnaire.

    Ceci te permet d'utiliser le même gestionnaire pour tous tes signaux mais même si ce n'était pas le cas, ça ne serait pas plus gênant : rien ne t'empêcherait d'écrire un gestionnaire par signal, qui réaliserait en fait la même tâche que ses collègue, laquelle consisterait elle-même à aller déposer le numéro correspondant dans la variable en question. Et tu n'aurais même pas besoin d'aller chercher sa valeur numérique explicite : tu pourrais continuer à utiliser les macros définies à cet effet comme tu le fais quand tu mets ton gestionnaire en place :

    Code C : 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
    void gestionnaire_sigint (int n)
    {
        variable = SIGINT;
    }
     
    void gestionnaire_sigterm (int n)
    {
        variable = SIGTERM;
    }
     
    void gestionnaire_sigusr1 (int n)
    {
        variable = SIGUSR1;
    }
     
    …

    Je conçois tout-à-fait qu'il faille un peu de pratique pour que tout cela devienne automatique, mais reconnais tout de même que tu aurais pu y penser tout seul.

    Par contre j'ai pas très bien compris comment on fait ça, c'est quoi exactement cette structure contexte ?
    Laisse tomber le contexte pour le moment. Tu peux très bien te contenter de définir une variable de type int et passer l'adresse de cette variable à sigaction().

    L'idée générale, qui répond en fait à un problème très courant en programmation, est de partir de ce fait, et remplacer par la suite le simple entier par une structure, qui elle est un peu plus sophistiquée. Ça ne change rien à l'architecture générale de ton programme (ni à la manière dont le problème a été résolu), mais que ça te permet de transmettre plus d'information si nécessaire (et dans les deux sens).

    C'est utile quand tu écris un programme qui contient plein de procédures diverses qui, chacune, ont besoin à chaque fois d'un certain nombre d'informations (toujours les mêmes) en plus des paramètres qui leur sont spécifiques. Il peut s'agir par exemple du nom et de l'identifiant de l'utilisateur en cours, et de la connexion à la base de données. Si tu écris une application web par exemple, ce sont des choses sur lesquelles tu vas t'appuyer à chacune de tes opérations. Donc le principe est de regrouper tout cela dans un seul objet, et de passer systématiquement (en général) cet objet en tant que premier paramètre d'une fonction, suivi des paramètres spécifiques.

    C'est aussi pratique quand, comme dans le cas des signaux mais plus généralement dans celui des fonctions de callback, tu n'appelles pas toi-même la fonction, et donc tu ne reçois pas non plus sa valeur de retour si celle-ci existe.

    Ce n'est d'ailleurs pas un concept normalisé et enseigné tel quel, mais c'est une idée qui vient naturellement à tout programmeur confronté à ce problème et, généralement, tout le monde l'appelle de la même façon (« contexte »).

    L'avantage de déclarer un objet et de passer un pointeur vers lui est double : non seulement tu évites tout recours aux variables globales mais, en plus, ça te permet de définir plusieurs contextes à la fois et de passer de l'un à l'autre au besoin. Tu peux donc automatiquement paralléliser ton programme sans avoir à y changer quoi que soit.

    Parce que si je peux récupérer tout les signaux qui ont été reçu alors je vais pouvoir faire le traitement correspondant dans mon automate et ça me facilitera grandement la tache
    Attention : tu peux récupérer le numéro du signal quel qu'il soit, mais uniquement si tu es à l'écoute de celui-ci, et tu ne récupéreras que le premier signal reçu jusqu'à réarmement du gestionnaire. Si tu reçois quinze fois le même signal d'affilée pendant que ton programme est occupé à autre chose, tu ne seras prévenu qu'une seule fois et les quatorze autres seront perdus. Encore, une fois, les signaux ne servent pas à faire du message passing, et d'une manière générale, ils ne servent pas à transmettre de l'information.

    Il faut voir les signaux (et leurs gestionnaires respectifs) comme des disjoncteurs.

    Par contre il y a aussi un autre problème c'est que le prof veut qu'il y ait un fichier qui crée plusieurs processus et qu'on fasse un exec vers d'autres fichiers qui contiennent les autres automates, donc chaque automate se trouve dans des fichiers séparés et ils doivent s'envoyer des signaux entre eux, et je ne sais pas comment faire pour avoir " des variables globales entre fichier " pck je pensais installer le gestionnaire pour tous les signaux qui doivent être modifiés dans le premier fichier celui qui crée tout mais comment faire pour passer les variables globales entre eux?
    Il faut recourir à extern mais je ne t'en dis pas plus car tu ne dois pas utiliser de variable globale du tout !

Discussions similaires

  1. [VB6] [MDI] Signaler la fermeture d'une fille à la mère
    Par cpri1shoot dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 13/04/2004, 08h57
  2. Comment Attendre la fin d'un Processus
    Par mr_titi dans le forum C++Builder
    Réponses: 3
    Dernier message: 05/06/2003, 16h35
  3. Réponses: 3
    Dernier message: 25/11/2002, 14h15
  4. Réponses: 1
    Dernier message: 14/11/2002, 00h09
  5. [VB6] attendre un événement pour continuer l'exécution
    Par Argonz dans le forum VB 6 et antérieur
    Réponses: 21
    Dernier message: 12/11/2002, 13h08

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