Précédent Remonter Suivant

2  Communication inter-processus par tube

Deux processus peuvent communiquer via un tube.

Le schéma de fonctionnement de base est le suivant ; on suppose que le père écrit dans le tube alors que le fils lit dans le tube (figure 3) :
  1. Le processus crée le tube ;
  2. Le processus fait un appel à fork() pour créer un fils.
  3. Les deux processus père et fils possèdent alors chacun un descripteur en lecture et en écriture sur le tube.
  4. Le processus père ferme son descripteur en lecture. Le processus fils ferme son descripteur en écriture sur le tube.
  5. Le processus père peut écrire sur le tube ; les valeurs écrites pourront être lues par le fils.


Figure 3 : Partage d'un tube entre un processus père et un processus fils


Pour établir une communication bidirectionnelle, on utilise deux tubes.

Le programme essai de la figure 4 lit des caractères sur l'entrée standard et envoie les lettres et chiffres à son processus fils. Le processus fils compte les lettres et les chiffres et affiche les résultats à la fin. Le processus père attend la terminaison du fils pour s'arrêter.

% cat essai.c
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>

int
main (void)
{
  pid_t retour ;
  int tube[2], lettre = 0, chiffre = 0 ;
  char k ;
  if (pipe (tube) == -1) { perror ("creation pipe impossible\n") ; exit (1) ; }
  switch (retour = fork ()) {
    case -1 : perror ("Creation impossible") ; exit(1) ;
    case 0  :
      printf ("processus fils\n") ;
      /* le tube est ici ferme en ecriture: le dernier descripteur ouvert   */
      /* en ecriture sur le tube sera dans le processus pere. Quand celui   */
      /* ci fermera ce descripteur, le read effectué par le fils renverra 0 */
      close (tube[1]) ;
      while (read (tube[0], &k, 1) >0)
        if (isdigit (k)) chiffre++ ;
        else lettre++ ;
      printf ("%d chiffres recus\n", chiffre) ;
      printf ("%d lettres recues\n", lettre) ;
      exit (0) ;
    default :
      printf ("pere: a cree processus %d\n", retour) ;
      close (tube[0]) ;
      while (read (0, &k, 1) >0)
        if (isalnum(k)) write (tube[1], &k, 1) ;
      /* le tube est ici ferme en ecriture : un read sur le */
      /* tube vide retournera 0 dans le processus fils      */
      close (tube[1]) ;
      wait (0) ;
      printf ("pere: a recu terminaison fils\n") ;
  }
}
% ./essai
pere: a cree processus 14643
processus fils
qw;12;r5^D
3 chiffres recus
3 lettres recues
pere: a recu terminaison fils

Figure 4 : Communication père/fils via un tube



Exercice 2   Donnez le code d'un programme dont le fonctionnement est le suivant : un processus père crée deux fils ; ces deux fils communiquent par un tube. Le premier fils envoie une suite de caractères au second fils. À la terminaison de la transmission, les deux fils terminent. Le père attend ses fils pour terminer lui-même.

Exercice 3   Soit le programme suivant : un processus père crée 3 fils et dialogue (père vers fils) avec chacun d'eux via un tube. Le père envoie des entiers que les fils lisent et affichent. Le programme de la figure 5 propose une solution initiale pour ce problème.

Expliquez pourquoi ce programme n'est pas correct.


#include <stdio.h>
#include <unistd.h>

#define NBFILS  3
#define MAXVAL  9

static void
creerFils (int descr)
{
  int val ;
  int pid ;

  /* mon numero de processus */
  pid = getpid () ; 
  
  /* Lecture de valeur depuis le descripteur et affichage */
  while (read (descr, &val, sizeof (int)) > 0) 
    printf ("Valeur %d par le processus %d\n", val, pid) ;

  /* Fermeture du descripteur */
  close (descr) ;
} /* creerfils () */ 

void
main ()
{
  int i, fils ; 
  /* les tubes pour chacun des fils */
  int tubes [NBFILS][2] ;

  /* Creation des tubes */
  for (fils=0 ; fils < NBFILS; fils++) 
    if (pipe (tubes [fils]) == -1) {
      perror ("pipe") ;
      exit (-1) ;
    }
  
  /* Creation des fils */
  for (fils=0 ; fils < NBFILS; fils++) 
    switch (fork()) {
      case -1 :
        perror ("fork") ;
        exit (-1) ;
      case 0 : /* fils */
        /* ferme le tube en ecriture */
        close (tubes [fils][1]) ;
        /* creation du fils */
        creerFils (tubes [fils][0]) ;
        /* terminaison du fils */
        exit (0) ;
      default : /* pere */
        /* ferme le tube en lecture */
        close (tubes [fils][0]) ;  
    }

  /* Emmision d'entiers vers les fils */
  for (fils=0, i=0; i < MAXVAL; i++, fils = i%NBFILS)
    write (tubes [fils][1], &i, sizeof (int)) ;

  /* Fermeture des tubes en ecriture */ 
  for (fils=0 ; fils < NBFILS ; fils++) 
    close (tubes [fils][1]) ;

  /* Attente de la terminaison des fils */
  for (fils=0 ; fils < NBFILS; fils++) 
    wait (0) ;
} /* main () */

Figure 5 : Programme erroné



Exercice 4   Étudiez la terminaison de la version de ce programme donnée à la figure 6.

#include <stdio.h>
#include <unistd.h>

#define NBFILS  3
#define MAXVAL  9

static void
creerFils (int descr)
{
  int val ;
  int pid ;

  /* mon numero de processus */
  pid = getpid () ; 
  
  /* Lecture de valeur depuis le descripteur et affichage */
  while (read (descr, &val, sizeof (int)) > 0) 
    printf ("Valeur %d par le processus %d\n", val, pid) ;

  /* Fermeture du descripteur */
  close (descr) ;
  
} /* creerfils () */ 

int
main ()
{
  int i, fils ; 

  /* les tubes pour chacun des fils */
  int tubes [NBFILS][2] ;

  /* Creation des tubes et des fils */
  for (fils=0 ; fils < NBFILS; fils++) {

    /* Creation du tube */ 
    if (pipe (tubes [fils]) == -1) {
      perror ("pipe") ;
      exit (-1) ;
    }

  /* Creation du fils */ 
    switch (fork()) {
      case -1 :
        perror ("fork") ;
        exit (-1) ;
      case 0 : /* fils */
        /* ferme le tube en ecriture */
        close (tubes [fils][1]) ;
        /* creation du fils */
        creerFils (tubes [fils][0]) ;
        /* terminaison du fils */
        exit (0) ;
      default : /* pere */
        /* ferme le tube en lecture */
        close (tubes [fils][0]) ;  
    }
  }
  
  /* Emmision d'entiers vers les fils */
  for (fils=0, i=0; i < MAXVAL; i++, fils = i%NBFILS)
    write (tubes [fils][1], &i, sizeof (int)) ;

  /* Fermeture des tubes en ecriture */ 
  for (fils=0 ; fils < NBFILS ; fils++) 
    close (tubes [fils][1]) ;

  /* Attente de la terminaison des fils */
  for (fils=0 ; fils < NBFILS; fils++) {
    fprintf (stderr, "Attente d'un fils...") ; 
    wait (0) ;
    fprintf (stderr, "ok\n") ; 
  }
} /* main () */

Figure 6 : Programme dont la terminaison est à étudier



Exercice 5   Un processus père lit des caractères sur l'entrée standard et filtre pour l'un des ses fils les lettres et pour l'autre les chiffres. Le premier fils calcule le nombre d'occurrences de chacune des lettres dans un tableau pendant que le second calcule la somme des chiffres. Lorsque le père atteint la fin de fichier sur l'entrée standard, il en avise ses fils qui envoient leurs résultats au père qui se charge de les afficher sur la sortie standard.


Précédent Remonter Suivant