Cours De Communication entre processus par tubes anonymes

ISTIC 2018-2019 

Communication entre processus par tubes anonymes 

Amira BELHEDI belhedi.amira@yahoo.fr 

Référence livre Programmation système en C sous Linux Signaux, processus, threads, IPC et sockets 

Communication par les tubes (pipes) 

2/33 

Communication par les tubes (pipes) 

– Les tubes ordinaires (anonymes): ne permettent que la 

communication entre processus issus de la même application (entre père et fils, ou entre frères). 

– Les tubes nommés: permettent la communication entre processus qui sont issus des applications différentes. (communication entre processus créés dans des applications différentes. 

3/33 

Caractéristiques 

– Les données sont lues dans l’ordre dans lequel elles ont été écrites dans le tube. 

– Une fois une donnée est lue elle sera supprimée. – Plusieurs processus peuvent lire dans le même tube mais ils ne liront pas les mêmes données, car la lecture est destructive. 

– Il y a une synchronisation type producteur/consommateur – si le tube est vide le lecteur (consommateur) attend qu’on écrit dans le tube avant de lire. – si le tube est plein, un écrivain (producteur) attend qu’il y a de la place pour pouvoir écrire. 

4/33 

Création d’un tube anonyme 

– l’un permet de lire dans le tube par la primitive 

«read()») . – l’autre permet d’écrire dans le tube par la primitive 

«write()»). 

5/33 

Tubes anonymes 

#include <unistd.h> int pipe (int p[2]); 

– Le descripteur d’indice 0 (p[0]) désigne la sortie du tube, il est ouvert en lecture seule. – Le descripteur d’indice 1 (p[1]) désigne l’entrée du tube, il est ouvert en écriture seule. 

6/33 

Tubes anonymes 

0 en cas de succès. -1 sinon (en cas d’échec). 

main () { int p[2]; pipe(p); /* création du tube p*/ …… } 

7/33 

Tubes anonymes 

close (p [1]); // fermeture du descripteur en écriture. close (p [0]); // fermeture du descripteur en lecture. 

8/33 

Ecriture dans un tubes anonymes 

– En cas de succès → write() retourne le nombre d’octets écrits. – En cas d’erreur → write() retourne -1. 

9/33 

Exemple 

#include <stdio.h> #include <stdlib.h> #define N 6 main () { 

char *c; int p[2]; int nb_ecrits; c=(char *)malloc(N*sizeof(char)); c= »ABCDEF »; pipe(p); /* création de tube */ if ((nb_ecrits=write(p[1],c,6)) = = -1) { 

printf(« Erreur d’ecriture dans le tube \n »); exit(0);} printf(« nb_ecrits = %d \n « , nb_ecrits);} 

10/33 

Tubes anonymes 

– jusqu’à ce qu’il y ait suffisamment de place pour pouvoir écrire dans le tube. – La place se libère par la lecture dans le tube 

11/33 

Tubes anonymes 

printf(“ erreur ecriture dans le tube”); else 

printf(“Pas d erreur”); printf(“ processus termine ”); } 

12/33 

Lecture dans un tube 

int read (int p[0], void *zone, int nb_car); – « P[0] » : descripteur du flux – « zone » : pointeur sur une zone mémoire dans laquelle les données seront écrites après lecture. – « nb_car »: nombre d’octets (caractères) que l’on souhaite lire à partir du tube. 

– En cas de succès, elle retourne le nombre d’octets effectivement lus. – En cas d’erreur cette fonction retourne -1 

13/33 

Exemple 

#include <stdio.h> #include <stdlib.h> #define N 6 main () { 

char *c, *s; int p[2]; int nb_lus, nb_ecrits; c=(char *)malloc(N*sizeof(char)); s=(char *)malloc(N*sizeof(char)); c= »ABCDEF »; pipe(p); /* création de tube */ 

14/33 

Exemple 

if ((nb_ecrits=write(p[1],c,N))==-1) { 

printf(« Erreur d’ecriture dans le tube \n »); exit(0);} printf(« nb_ecrits = %d \n « , nb_ecrits); if ((nb_lus=read(p[0],s,N))==-1) { 

printf(« Erreur de lecture dans le tube \n »); exit(0);} else if (nb_lus==0) { 

printf(« pas de caractère lu \n »); exit(0);} printf( » la chaîne lue est : %s \n », s); } 

15/33 

Lecture dans un tube 

– Si le tube n’a pas de rédacteur (le descripteur en écriture est fermé) alors la fonction « read() » retourne 0 caractères lus. – Si le tube a un rédacteur, alors il reste bloqué jusqu’à ce que le tube ne soit pas vide. 

16/33 

Exemple: Lecture dans un tube vide avec rédacteur 

void main () { char c; int p[2]; int nb_lus; pipe (p); // création de tube if ((nb_lus==read(p[0],&c,1))==-1) 

printf(« erreur lecture dans le tube »); else if (nb_lus==0) 

printf(« Pas de caractère lu \n »); printf(« processus termine \n »); }→ Ce programme reste bloqué. 

17/33 

Exemple: Lecture dans un tube vide sans rédacteur 

void main () { char c; int p[2]; int nb_lus; pipe (p); // création de tube close(p[1]); if ((nb_lus==read(p[0],&c,1))==-1) 

printf(« erreur lecture dans le tube »); else if (nb_lus==0) 

printf(« Pas de caractère lu \n »); printf(« processus termine \n »); } 

Résultat Pas de caractère lu 

processus termine → il n’y a pas de producteur (close(p[1]);), la fonction «read()» retourne 0. 

18/33 

Communication entre 2 processus 

  1. Le processus crée le tube ; 2. Le processus fait un appel à fork() pour créer un fils. →Le père et le fils possèdent chacun un descripteur en lecture et en écriture sur le même tube 3. Le père ferme son descripteur en lecture. Le fils ferme son descripteur en écriture sur le tube. 4. Le processus père peut écrire sur le tube ; les valeurs écrites pourront être lues par le fils. 

19/33 

Tubes anonymes 

– Ecrire un programme c qui permet de créer un 

processus fils. 

20/33 

#include <stdio.h> #include <unistd.h> #define TAILLE 30 main () {char envoi [TAILLE], reception [TAILLE]; 

int p [2], i, pid,val, etat; strcpy ( envoi, « texte transmis au tube »); val=pipe(p) ; if (val==-1) { 

printf (« erreur de creation du tube « ); exit (1); }else { pid = fork(); 

if (pid == -1) { 

printf(« erreur de creation du fils »); exit (2); } 

if (pid > 0) /* le père écrit dans le tube */ 

close(p[0]) ; write (p[1], envoi, TAILLE); close (p[1]) ; wait (&etat); } 

if (pid == 0) /* le fils lit à partir du tube */ 

{close(p[1]); 

read (p[0], reception, TAILLE); printf ( » –> %s\n », reception); close (p[0]); exit (0); } } } 

Duplication de descripteurs 

– « dup » – « dup2 » 

#include <unistd.h> int dup(int ancien_fd) int dup2(int ancien_fd, int nouveau_fd) 

– créent une copie du descripteur « ancien_fd ». – retournent 

23/33 

Duplication de descripteurs 

Valeur entière Nom int dup(int ancien_fd) 

0 (STDIN_FILENO) clavier – copie « ancien_fd » dans 

1 la première entrée libre 

de la table des descripteurs 

(STDOUT_FILENO) 2 ……….. Ecran Ecran 

……………… 

…………. …………………. int dup2(int ancien_fd, int nouveau_fd) – Ferme le descripteur « nouveau_fd » – remplace nouveau_fd par « ancien_fd » 

24/33 

Duplication de descripteurs 

– l’ancien et le nouveau descripteurs peuvent être utilisés 

d’une manière interchangeable. – Ces primitives sont 

25/33 

Tubes anonymes 

– Ecrire un programme équivalent à la commande Shell suivante: ls|wc 

– Pour cet exercice vous allez utiliser 

26/33 

Primitive exec() 

– permet le lancement de l’exécution d’un programme externe provenant d’un fichier binaire. – permet le recouvrement d’un processus par un autre exécutable – Il n’y a pas création d’un nouveau processus, mais simplement changement de programme. – Remplace l’image mémoire du processus en cours par un nouveau processus. – L’identité du processus étant conservée. 

– Une des variante est la primitive execlp() 

27/33 

Primitive execlp() 

– int execlp(char *fiche, char *arg0,char *arg1,…,char *argn, NULL) 

– « fiche » indique le nom du programme à exécuter: le programme à exécuter est situé dans un chemin de recherche de l’environnement 

28/33 

Primitive execlp() 

– Lancer la commande « ls -l /tmp » à partir d’un programme. 

#include <stdio.h> #include <unistd.h> int main(void){ execlp(« ls », »ls », »-l », »/tmp », NULL) ; perror(« echec de execlp \n »); } 

29/33 

#include <stdio.h> #include <unistd.h> int main() { 

int p[2]; /* ouverture d’un pipe */ if(pipe(p)) { 

printf (« pipe »); exit(1); }switch (fork()) {case -1 : /* erreur */ printf ( » pb fork « ); exit(1); break; 

1ère version avec dup2 

programme équivalent à la commande Shell suivante : ls|wc 

30/33 

1ère version avec dup2 

case 0 : /* le processus fils execute la commande ls */ 

/* la sortie standard du processus est redirigee vers le tube */ 

dup2(p[1],1);//copie le descripteur p[1] en STDOUT_FILENO, en fermant 

STDOUT_FILENO auparavant s’il était ouvert close (p[1]); close (p[0]); /* le processus ne lit pas dans le tube */ execlp (« ls », « ls », NULL); printf (« pb execlp(ls) »); exit(1); break; 

default : /* le processus pere execute la commande wc */ 

/* l’entree standard du processus est redirigee vers le tube */ dup2(p[0],0); //copie le descripteur p[0] en STDIN_FILENO, en fermant 

STDIN_FILENO auparavant s’il était ouvert 

close (p[0]); close (p[1]); execlp (« wc », « wc », NULL); 

printf (« pb execlp(wc) »); exit(1); break; } } 

#include <stdio.h> #include <unistd.h> int main() { 

int p[2]; /* ouverture d’un pipe */ if(pipe(p)) { 

printf (« pipe »); exit(1); }switch (fork()) {case -1 : /* erreur */ printf ( » pb fork « ); exit(1); break; 

2ère version avec dup 

programme équivalent à la commande Shell suivante : ls|wc 

32/33 

case 0 : /* le processus fils execute la commande ls */ 

/* la sortie standard du processus est redirigee vers le tube */ close (STDOUT_FILENO); // ou close (1); dup(p[1]); // copie le descripteur p[1] vers la 1ere entrée libre de la table des descripteurs 

(ici STDOUT_FILENO) close (p[1]); close (p[0]); /* le processus ne lit pas dans le tube */ execlp (« ls », « ls », NULL); printf (« pb execlp(ls) »); exit(1); break; 

default : /* le processus pere execute la commande wc */ 

2ère version avec dup 

/* l’entree standard du processus est redirigee vers le tube */ close (STDIN_FILENO);// ou close (0); dup(p[0]); // copie le descripteur p[1] vers la 1ere entrée libre de la table des descripteurs 

(ici STDOUT_FILENO) close (p[0]); close (p[1]); execlp (« wc », « wc », NULL); printf(« pb execlp(wc) »); exit(1); break; } } 

télécharger gratuitement Cours De Communication entre processus par tubes anonymes

Quitter la version mobile