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) :
-
Le processus crée le tube ;
- Le processus fait un appel à fork() pour créer un
fils.
- Les deux processus père et fils possèdent alors chacun un
descripteur en lecture et en écriture sur le tube.
- Le processus père ferme son descripteur en lecture. Le
processus fils ferme son descripteur en écriture sur le tube.
- 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.