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 :

Mauvaise communication père/fils signaux


Sujet :

C

  1. #1
    Membre régulier
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2008
    Messages
    138
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2008
    Messages : 138
    Points : 70
    Points
    70
    Par défaut Mauvaise communication père/fils signaux
    Bonjour

    Voilà, souhaitant revisiter les concepts de communications entre processus via des signaux, j'ai écrit un petit programme qui crée un fils.
    Ensuite, le père et le fils doivent écrire chacunleur tour un nombre qui s'incrémente indépendemment.
    Le souci à l'exécution de ce programme est que je n'obtiens pas une alternance parfaite. En effet, le père exécute 4 fois de suite son évènement avant que le fils n'en exécute qu'un.

    J'obtiens donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    JEU D ESSAI :
     
    pere n = 1
    pere n = 2
    pere n = 3
    pere n = 4
    fils n = 1
    pere n = 5
    ...
    pere n = 10
    J'ai eu beau farfouillé, impossible de mettre le doigt sur mon erreur. Quelqu'un détecte-t-il la faille ? Merci d'avance

    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
     
     
    struct sigaction act_min, act_maj;
    pid_t pidFils, pidPere, pid;
     
     
    //affichage par le fils
    void handler_min(int x){
     
        static int n = 1;
     
    	printf("fils n = %d\n", n);
    	//on incrémente le nombre de lettres à afficher la prochaine fois
    	n++;
    	kill(pidPere,SIGUSR1);//on appelle le pere
    }
     
     
     
    //affichage par le père
    void handler_maj(int x)
    {
        static int n = 1;
     
    	printf("pere n = %d\n", n);
     
    	//test condition de fin du programme
    	if (n <= 10)
    	{
    		n++;//on incrémente le nombre de lettres à afficher la prochaine fois
    		kill(pidFils,SIGUSR1);//on appelle le fils
    	}
    	else
    		exit;	
    }
     
    main(){
    	pid=fork();
    	if(pid==0)
    	{
    		/*processus fils*/
    		//enregistrement des PID du père et du fils
    		pidFils = getpid();
    		pidPere = getppid();
    		act_min.sa_handler=handler_min;//on enclenche le signal
    		sigaction(SIGUSR1,&act_min,NULL);
    		while(1)
    		{
    			pause();
    		}
     
    	}else{
    		/*processus pere*/
    		sleep(1);//pour que ce soit le fils qui commence
    		act_maj.sa_handler=handler_maj;//on enclenche le signal
    		sigaction(SIGUSR1,&act_maj,NULL);
    		kill(pidFils, SIGUSR1);//on envoie le premier signal au fils
    		while(1)
    		{
    			pause();
    		}
     
    	}
    }
    Merci d'avance

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 400
    Points : 23 779
    Points
    23 779
    Par défaut
    Citation Envoyé par Shargat Voir le message
    Bonjour

    Voilà, souhaitant revisiter les concepts de communications entre processus via des signaux, j'ai écrit un petit programme qui crée un fils.
    Je ne sais pas si c'est ce que tu cherches à faire au final mais il est bon de rappeler que si les signaux UNIX comptent au nombre des mécanismes permettant l'IPC, dans le sens, où un signal envoyé depuis un processus peut être reçu par un autre, ils ne servent absolument pas à échanger des données, et ne doivent être utilisés qu'occasionnellement.

    C'est une erreur fréquente dont on discutait déjà ici, ici, ici et .

    Ensuite, le père et le fils doivent écrire chacunleur tour un nombre qui s'incrémente indépendemment.
    Le souci à l'exécution de ce programme est que je n'obtiens pas une alternance parfaite. En effet, le père exécute 4 fois de suite son évènement avant que le fils n'en exécute qu'un.
    Alors, à première vue, tu initialises le sighandler() du père après sleep(1). Donc le premier signal envoyé par le fils est perdu. D'autre part, par défaut, lorsque tu te trouves déjà dans le handler d'un signal reçu, ledit signal est bloqué s'il arrive à nouveau.

    Dans ce dernier cas, les signaux en question peuvent s'accumuler, mais peuvent aussi être perdus si la file est pleine.

    Mais surtout, printf() est bufferisée. Chaque processus a son propre tampon même si, en principe, il est vidé à chaque nouvelle ligne, et chacun d'eux écrit sur le même terminal. L'ordre d'apparition n'est pas garanti.

    ÉDIT : d'autre part :


    Ceci est syntaxiquement correct, mais reste une erreur de programmation : exit() est une fonction et pas un mot-clé. Cette déclaration n'aura donc aucun effet et ton processus continuera à exister comme si la variable n'avait pas atteint 10.

  3. #3
    Membre régulier
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2008
    Messages
    138
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2008
    Messages : 138
    Points : 70
    Points
    70
    Par défaut
    Bonjour

    Merci pour ta réponse.

    Je ne sais pas si c'est ce que tu cherches à faire au final mais il est bon de rappeler que si les signaux UNIX comptent au nombre des mécanismes permettant l'IPC, dans le sens, où un signal envoyé depuis un processus peut être reçu par un autre, ils ne servent absolument pas à échanger des données, et ne doivent être utilisés qu'occasionnellement.
    C'est noté, mais cette utilisation n'est qu'à titre purement pédagogique pour bien comprendre l'utilisation des signaux et processus.

    Alors, à première vue, tu initialises le sighandler() du père après sleep(1). Donc le premier signal envoyé par le fils est perdu. D'autre part, par défaut, lorsque tu te trouves déjà dans le handler d'un signal reçu, ledit signal est bloqué s'il arrive à nouveau.
    Je ne comprends pas cela car c'est bien le père qui envoie le premier le signal au fils. Le fils se contente d'initialiser ses handler et de se mettre en pause. C'est donc forcément lui qui reçoit le premier signal, non le père. En quoi le sleep(1) est-il gênant du compte tenu de cela ?

    Mais surtout, printf() est bufferisée. Chaque processus a son propre tampon même si, en principe, il est vidé à chaque nouvelle ligne, et chacun d'eux écrit sur le même terminal. L'ordre d'apparition n'est pas garanti.
    J'ai en effet rencontré ce problème et ai ajouté fflush(stdout); après chaque printf() (bien qu'apparemment, un retour chariot provoque le vidage du tampon par défaut).

    En résumé, si la réponse à mon problème est là, je ne l'ai pas compris.

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 400
    Points : 23 779
    Points
    23 779
    Par défaut
    En fait, j'ai moi-même posté un peu vite. C'est dû à d'autres choses, également :

    • Tu n'initialises pas tous les champs de la structure sigaction et tu ne peux pas considérer non plus que tous les champs sont à zéro au moment de la déclaration. Ils sont dans un état indéfini. Cela concerne notamment les flags sa_flags qu'il faut mettre à zéro, sinon le comportement du père et du fils sera indéterminé, surtout à la fin de l'un d'eux. Sur ma machine, le processus fils reste en vie après la fin du père ;
    • D'autre part, le champ sa_mask spécifie des signaux à bloquer. Il se peut tout-à-fait que tu bloques d'autres signaux lorsque tu te trouves dans ton handler, notamment ceux concernant les segfaults ;
    • Dans les flags encore une fois, si le flag SA_SIGINFO est mis, c'est dans le champ sa_sigaction que l'on puisera l'adresse du handler à la place de sa_handler. Si l'état de tes flags est indéfini, tu ne peux pas savoir si tu as réellement installé ton handler ou pas, et tu risques un plantage ;


    Mais surtout :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        else
        {
    
            printf ("FILS : %d\n",pidFils);
            /*processus pere*/
    
            sleep(1);//pour que ce soit le fils qui commence
    
            act_maj.sa_handler=handler_maj;//on enclenche le signal
            sigaction(SIGUSR1,&act_maj,NULL);
    
            kill(pidFils, SIGUSR1);//on envoie le premier signal au fils

    pidFils est initialisé du côté du processus fils, mais pas de celui du processus père. Lorsque tu fais fork(), les deux processus vivent leur vie indépendamment l'un de l'autre et ne partagent pas du tout leur mémoire. Tu ne sais donc pas à quel processus tu envoies le premier signal. La valeur de pidFils est totalement indéfinie, mais il se peut que ce soit zéro. Et dans ce cas :

    Code man : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    DESCRIPTION
           The kill() system call can be used to send any signal to any process group or process.
     
           If pid is positive, then signal sig is sent to pid.
     
           If pid equals 0, then sig is sent to every process in the process group of the calling process.
     
           If pid equals -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init), but see below.
     
           If pid is less than -1, then sig is sent to every process in the process group -pid.
     
           If sig is 0, then no signal is sent, but error checking is still performed.

    Donc, il y a des chances que tu t'envoies ton signal à la fois à toi-même et à tous les processus de ton groupe. Celui sera bloqué car tu trouveras dans le handler concerné − au passage, faire un kill() dans un gestionnaire de signal est une TRÈS mauvaise idée − mais dès que tu en sortiras, le signal en attente sera prioritaire sur beaucoup de choses et, tant que ton quantum de temps ne sera pas écoulé, tu vas y entrer à nouveau, avant même que le fils n'ait eu la chance de dire quelque chose.

    Ce n'est pas le seul bug : comme les flags et les masques sont complètement indéfinis et que le processus père s'exécute pratiquement en une seule fois, il y a des chances pour que le fils soit dans son handler de signal lui-aussi, bloque les autres, et arrive à perdre le signal de terminaison dû à la mort de son père, et ton fils continue à vivre sa vie tranquillement en tâche de fond :-)

    Trouve-le avec « ps aux » et essaie de lui envoyer « kill -SIGUSR1 <numéro> » à la main plusieurs fois, pour rigoler !

    Bref, beaucoup de bugs et un programme un peu bancal. Au passage, sa_mask ne s'initialise pas directement, mais en utilisant les fonctions de sigsetops (en l'occurence sigemptyset()).

    Voir aussi ce fil, dans lequel l'auteur fait quelque chose de similaire.

  5. #5
    Membre régulier
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2008
    Messages
    138
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2008
    Messages : 138
    Points : 70
    Points
    70
    Par défaut
    Merci beaucoup pour cette réponse très complète et pertinente.

    J'avais en effet omis de considérer les deux espaces mémoires distincts de ces deux processus. C'était bien l'intérêt de ce petit programme.

    Je tiens également compte des bugs énoncés. Je fais désormais les kill() dans le main et vais me pencher sur les flags.

    Merci et bonne journée

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Communication père/fils tableau d'entiers.
    Par boblinux dans le forum Débuter
    Réponses: 13
    Dernier message: 26/11/2014, 16h09
  2. Gtk et communication père fils
    Par troumad dans le forum GTK+ avec C & C++
    Réponses: 9
    Dernier message: 22/04/2011, 22h04
  3. Champ père-Fils
    Par marie10 dans le forum Access
    Réponses: 3
    Dernier message: 28/02/2006, 13h17
  4. [pthread] relation père - fils ?
    Par jedimind dans le forum C
    Réponses: 3
    Dernier message: 14/11/2005, 11h18
  5. [MFC] multithread, communication père<->fils
    Par Joeleclems dans le forum MFC
    Réponses: 19
    Dernier message: 19/05/2005, 10h31

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