Anúncio
Anúncio

Mais conteúdo relacionado

Anúncio

Tubes.pdf

  1. Communication entre processus et Synchronisation des processus M. Ben Salah 1
  2. - Les processus concurrents peuvent être : - coopératifs : dépendants les uns des autre - ou indépendants - Les processus peuvent coopérer : - par communication de données - ou en partageant des ressources (mémoire, …)  Besoin de communiquer entre processus pour échanger des données et informations,  Besoin de synchronisation .
  3. I- Communication entre processus
  4. ● Il existe plusieurs moyens de communication entre processus : - Par le biais d’un fichier - tube - Signaux - Par une zone mémoire - Files de messages (MSQ) I- Communication entre processus - Files de messages (MSQ) 4
  5. I-1- Communication par tubes 5 ● Objectif de cette parties : permettre la communication entre processus.
  6. Communication par tubes ● Définition du tube (pipe) : Un tube est un fichier spécial géré par le système au niveau du système de gestion de fichiers. Il permet la communication entre deux processus de la manière suivante : un processus peut écrire des données dans le tube et un autre processus peut lire ces données. Processus écrivain : p1 Processus lecteur : P2 p2 6 ● Principe : deux extrémités (deux descripteurs) : une permettant d’´ecrire des données par le processus écrivain l’autre permettant de les lire par le processus lecteur ● On lit/écrit dans un tube comme on lit/écrit dans un fichier ordinaire
  7. ● Communication d’un flot continu de caractères - communication en mode flot (opposé à datagramme) : opérations de lecture indépendantes des opérations d’écriture ● Le fonctionnement est de type FIFO : - premier caractère écrit, premier caractère lu - Il n’est pas possible de lire un octet sans avoir lu tous ceux qui le précèdent ● Caractéristiques techniques : Communication par tubes ● Opération de lecture est destructive : une information lue est extraite du tube ● Si un tube est vide, ou si on a plus écrivains (extrémité d’écriture fermé) la lecture renvoie la valeur 0 (fin de fichier atteinte) ● Un processus qui écrit dans un tube qui n’a plus de lecteurs (extrémité de Lecture fermée ) reçoit le signal SIGPIPE ● Système assure la synchronisation entre le lecteur et l’écrivain : Bloque le processus lecteur quand le tube est vide. Bloque le processus ´écrivain quand le tube est plein 7
  8. ● Deux types de tubes : 1- Tubes anonymes 2- Tubes nommés Communication par tubes 8
  9. P1 (écrire) P2 (lire) Tubes anonymes ● Caractéristiques : ● Un tube annonyme est un fichier particulier : - Il n’a pas de nom dans l’arborescence des fichiers - Il est connu par deux descripteurs (lecture, écriture) ● Permet la communication entre processus qui ont un lien de parenté : les deux processus doivent connaitre le même canal afin de communiquer == > héritage du descripteur desc[1] desc[0] 9 == > héritage du descripteur ● temporaires (volatile) : ils n’existent que le temps d’exécution du processus qui les crée.
  10. ● Création : par appel systéme pipe, déclarée dans unistd.h ● Primitive : #include <unistd.h> int desc[2]; int pipe(int desc); Processus écrivain Processus lecteur Création de tubes anonymes retourne 0 si le tube est donc créé, -1 si en cas d’echec ► l'appel pipe retourne deux descripteurs : - desc[0] : Pour la lecture. C’est la sortie du tube dans laquelle on peut lire des données. Il doit être ouvert pour la lecture seule - desc[1] : Pour l’écriture. C’est l’entrée du tube dans laquelle on peut écrire des données. Il doit être ouvert pour l’écriture seule desc[1] desc[0] 10 ● Les descripteurs du tube doit être partagé par les processus communiquant == > l’appel pipe doit se faire avant fork() !!!
  11. desc[0] Création de tubes anonymes desc[1] 11
  12. ● Le tube doit être créé avant le fork pour que les descripteurs du tube soient hérités par les processus qui vont communiquer via le tube (héritage des descripteurs lors du fork() ) : Un tube est créé dans un seul processus. Le processus fils ne peut pas deviner les valeurs du tableau descripteur. Il faut donc appeler la fonction pipe avant d’appeler la fonction fork. Cela permet au processus père et le(s) processus fils (sa descendance) d’avoir les mêmes descripteurs de tubes, et pourront donc communiquer entre eux. Création de tubes anonymes et fork() communiquer entre eux. ● Rappelons qu’un tube ne permet de communiquer que dans un seul sens. Si l’on souhaite que les processus communiquent dans les deux sens, il faut créer deux pipes. 12
  13. Si le processus père écris, le processus fils lis : Partage d’un tube Processus lecteur (exemple : fils) hérite de desc[1] et desc[0] desc[1] desc[0] Processus écrivain (exemple : père) desc[1] desc[0] close(desc[0]) close(desc[1]) 1) pipe() 2) fork() 13 int close(int desc); == > - close(desc[1]) dans le processus lecteur - close(desc[0]) dans le processus écrivain tube Extrémité d’écriture Extrémité de lecture ● Il faut fermer le descripteur inutile dans chaque processus : primitive close -- > sinon interblocage close(desc[0]) close(desc[1])
  14. Ecriture/lecture dans un tube anonyme ● Pour écrire dans un tube : write ● Un tube à une capacité max : notée PIPE_BUF. C’est la taille du tube sur le système == > <limits.h> ● le signal SIGPIPE est envoyé au processus qui exécute write si le tube n’a ● Primitive : int write(int desc[1], const void *bufW, int nb_ecris); - nb_ecris : nombre d’octet à écrire - bufW : pointeur générique vers la mémoire contenant les données à écrire - l’appel retourne en résultat le nombre d’octets écris ● le signal SIGPIPE est envoyé au processus qui exécute write si le tube n’a été ouvert en lecture par aucun autre processus (pas de lecteur). Ce signale termine le processus. 14 ● La lecture : read ● Primitive : int read(desc[0], char *buffR, int taille_lecture); - BufR : pointeur vers la mémoire contenant les données à lire - taille_lecture : le nombre d’octets qu’on souhaite lire depuis la sortie du tube desc [0] à partir du tampon bufR
  15. # include <stdio.h> # include<sys/wait.h> # include<unistd.h> # include <stdlib.h> int main() { int desc[2], status, pid, n,n_lus; int taille=3; char chaine[taille+1]; pipe(desc); pid=fork(); if(pid==0) {// lecteur close (desc[1]); while((n_lus=read (desc[0], &chaine, taille))){ Exemple : Lecture 3 caractères au plus à chaque fois qu’il y a des données à la sortie du tube Tube anonyme :Application while((n_lus=read (desc[0], &chaine, taille))){ chaine[n_lus]='0'; printf (" message reçue : %s n", chaine); } close (desc[0]); }else {// Ecrivain close (desc[0]); n=write (desc[1], "bonjour", 7); close (desc[1]); wait(&status); } return 0; } 15 Ou }else {// Ecrivain close (desc[0]); char buf[14]; fgets(buf,sizeof(buf),stdin); //donnée s à partir du clavier //printf("len=%d n",(int) strlen(buf)); n_lus=write (desc[1], buf, (int) strlen(buf)); close (desc[1]); wait(&status); }
  16. Exercice 1 : Ecrire un programme utilisant un tube anonyme permettant au père de saisir un entier, le transmettre au fils. Le fils calcule son double et l’affiche . Tube anonyme :Application 16
  17. int main(){ pid_t pid_fils; int status, p1; int desc[2]; p1=pipe(desc); pid_fils=fork(); if(pid_fils==-1){ printf(" erreur fork () n"); exit(1); } if(pid_fils==0){ int n_lu,x; float res; sleep(2); #include <stdio.h> #include <unistd.h> #include<sys/types.h> #include <stdlib.h> #include<sys/wait.h> #include <errno.h> #include<math.h> 17 sleep(2); close(desc[1]); n_lu=read(desc[0],&x,sizeof(float); close(desc[0]); printf("double = %d n",2*x); }else{ int n_ec; int x; close(desc[0]); printf("saisir x: n"); scanf("%d",&x); n_ec=write(desc[1],&x,sizeof(int); close(desc[]); waitpid(pid_fils,&status, 0); } return 0; }
  18. Communication bidirectionnelle Exercice : Ecrire un programme permettant à un père d’envoyer un message « bonjour fils» au fils et le fils répond par « bonjour père ». Tube anonyme : Application 18
  19. int main(){ pid_t pid_fils; int status, p1,p2, desc[2],desc1[2]; p1=pipe(desc); p2=pipe(desc1); pid_fils=fork(); if(pid_fils==0){ //le fils int n_lu; #include <stdio.h> #include <unistd.h> #include<sys/types.h> #include<stdlib.h> #include<sys/wait.h> # include <string.h> Communication bidirectionnelle Une solution : }else{ int n_ec; char buffw[20], buffR[100]; close(desc[0]); close (desc1[1]); int n_lu; char buff[20]; close(desc[1]); close(desc1[0]); // lecture du message par le fils n_lu=read(desc[0],buff,sizeof(buff)); printf("message du pere : %s n",buff); //Le fils repond printf("fils saisir : n"); fgets(buff,13,stdin); write(desc1[1], buff, strlen(buff)); close(desc[0]); close(desc1[1]); 19 close (desc1[1]); printf("saisir un message : n"); fgets(buffw,13,stdin); // envoi d’un message au fils n_ec=write(desc[1],buffw, strlen(buffw)); // lecture de la réponse du fils read(desc1[0],buffR,sizeof(buffR)); close(desc[1]); close(desc1[0]); wait(&status); } return 0; }
  20. int main() { int desc1[2], desc2[2], status, pid,n; char mesg[13], rep[13]; pipe(desc1); pipe(desc2); pid=fork(); if(pid==0) { close (desc1[1]); else { close (desc1[0]); close (pip2[1]); write (desc1[1], "bonjour fils", 12); close(desc1[1]); read (desc2[0], rep, sizeof(rep)); close (desc2[0]); printf ("reponse reçue par le père : %s", rep); exit(0); } Communication bidirectionnelle Une solution : close (desc1[1]); close (desc2[0]); n=read (desc1[0], mesg, sizeof(mesg)); close(desc1[0]); printf ("message recue par le fils : %s", mesg); write (desc2[1], "bonjour pere", 12); close (desc2[1]); exit(0); } } return 0; } 20
  21. Exercice 3: Ecrire un programme permettant au père de saisir un ensemble d’entiers et de les transmettre au fils. Le fils calcul la moyenne et l’affiche Améliorer le programme précédent pour permettre au fils de transmettre la moyenne calculée, précédemment, au père puis le père l’affiche. Tube anonyme : Application 21 21 moyenne calculée, précédemment, au père puis le père l’affiche.
  22. ●Objectif : Les redirections ont pour effet de rediriger un flux vers un autre flux, typiquement les flux standards d'entrée/sortie : stdin, stdout et stderr. ● Mise en œuvre en utilisant les primitives : dup2, dup Les appels dup et dup2 permettent de dupliquer des entrées de la table des descripteurs du processus ● dup : #include <unistd.h> Redirection #include <unistd.h> int dup(int desc1); recopie dans la première entrée libre du tableau des descripteurs l’entrée correspondante au descripteur desc1. 22 ● dup2 : #include <unistd.h> int dup2(int fd1, int fd2); == > fd1 et fd2 désignent le même fichier, c’est le fichier désigné par fd1 avant l'appel. Si fd2 désigne le descripteur d’un fichier ouvert, celui-ci est préalablement fermé (close(fd2)) avant duplication.
  23. Rediriger les flots d’entrées-sorties vers des tubes Un usage pratique : ● Possibilité de lier la sortie tube[0] du tube à l’entrée standard (stdin). Par la suite, tout ce qui sort du tube arrive sur le flot d’entrée standard Pour se faire : dup2(tube[0], STDIN_FILENO); Ou dup2(tube[0], 0); ● Idem lier l’entrée du tube avec la sortie standard : dup2(tube[1], STDOUT_FILENO); ou dup2(tube[1], 1); 23
  24. Exemple le père est le lecteur : (1) pipe(desc); (2) fork( ); (3) close(desc[1]); (4) dup2(desc[0], STDIN_FILENO); Pour l’écrivain (un fils): (3) close(desc[0]); (4) dup2(desc[1], STDOUT_FILENO); (5) close(desc[1]);  redirige la sortie stdout vers l’entrée du tube Rediriger les flots d’entrées-sorties vers des tubes 24 24 (4) dup2(desc[0], STDIN_FILENO); (5) close(desc[0]);  Lecture de Stdin: Redirection de la sortie du tube vers sdtin : on obtient donc les caractères écrits par le processus-fils dans stdout tube 24
  25. Table de descripteurs du processus fils Table de descripteurs du processus père Application : cat file | wc –l Rediriger les flots d’entrées-sorties vers des tubes 25 close (desc[0]); dup2(desc[1],STDOUT_FILENO); close (desc[1]); execl ("/bin/cat", "cat", "file1",NULL) close (desc[1]); dup2(desc[0],STDIN_FILENO); close (desc [0]); execlp ("wc", "wc", "-l", NULL) cat file wc –l
  26. int main (int argc, char ** argv) { int tubeDesc[2]; pid_t pid_fils; if (pipe (tubeDesc) == -1) { perror ("pipe"); exit (1); } if ( (pid_fils = fork ( )) == -1 ){ perror ("fork"); exit (1); } if (pid_fils == 0) { /* fils */ else { /* pere*/ close (tubeDesc[1]); dup2(tubeDesc[0],0); if (execlp("wc","wc", "-l", NULL) == -1) { exit (1); } } return 0; } 26 if (pid_fils == 0) { /* fils */ close (tubeDesc[0]); dup2(tubeDesc[1],1); char* file="file1"; if (execl("/bin/cat", "cat",file,NULL) == -1) { //execl("/bin/cat", "cat", "clone1.c",NULL); exit (1); } } }
  27. ● Restriction des tubes anonyme : fonctionnement avec des processus issus d’un ancêtre commun ● Principal avantage : Les mécanismes de communication mis en jeu dans le noyau sont généraux. ● Pour s’affranchir de la restriction : les processus doivent désigner le tube qu’ils souhaitent utiliser. == > utilisation du système de fichiers : Un tube nommé 27
  28. Tube nommé 28
  29. ● On peut faire communiquer deux processus à travers un tube nommé. ● Géré par le système de fichiers : Un tube nommé correspond à un fichier avec un nom. Il passe donc par un fichier sur le disque ● Intérêt : les deux processus n’ont plus besoin d’avoir un lien de parenté Tube nommé 29 ● Etapes : - Création : mkfifo() - Ouverture : open() - Lecture / écriture : read() et write() - Fermeture : close() - Destruction : unlink()
  30. ● Création d'un tube nommé avec commande shell : mkfifo [-p] [-m mode] fileTube -m mode : droits d'accès (même que chmod) -p : création automatique de tous les répertoires intermédiaires dans le chemin fileTube Création d’un tube nommé Exemple : entrée sortie écriture lecture 30 $> cat file2 >tube1 $> cat <tube1 affichage du contenu de file2 écriture lecture Exemple :
  31. Création d’un tube nommé ● Appel système de création : mkfifo Exemple : #include <sys/types.h> #include <sys/stat.h> int mkfifo (const char *nom, mode_t mode); - nom : nom du tube nommé - mode: correspond aux droits d’accès associés au tube 31 Exemple : Créez un tube nommé caractérisé par : - propriétaire : possède toutes les permissions, - le groupe : les droits de lecture et d'écriture - les autres : aucun droits. Solution 1 : mkfifo("essai", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP) Solution 2 : mkfifo("essai", 0760)
  32. Exploitation : solution 1 : int f; mode_t mode; mode = S_IRUSR | S_IWUSR | S_IXUSR ; f=mkfifo("essai", mode); // f=mkfifo(("essai"), S_IRUSR | S_IWUSR | S_IXUSR ); if (f== -1){printf("Erreur de création du tube"); Création d’un tube nommé if (f== -1){printf("Erreur de création du tube"); exit(1); } ou: int f; f=mkfifo(("essai"), 0760); if (f== -1){ printf(stderr, "Erreur de création du tube"); exit(1); } 32
  33. Ouverture d’un tube nommé ● Primitive : int open (const char *nom, int mode_ouverture); renvoie -1 si erreur, sinon une valeur de type int (!=-1) c’est la valeur du descripteur que l'on attribue l'extrémité du tube en question. C’est le descripteur correspondant au mode d’ouverture spécifié. ● Ensuite, il faut ouvrir l'entrée/la sortie du tube avec la fonction open : descripteur correspondant au mode d’ouverture spécifié. - Le premier argument est le nom du tube nommé -Le second argument indique si c'est l'entrée ou la sortie du tube (mode d’ouverture). - mode d’ouverture : Il existe deux constantes pour cela, déclarées dans fcntl.h : O_WRONLY : pour l'entrée ; O_RDONLY : pour la sortie. 33
  34. Ensuite, on peut écrire et lire avec write et read comme si c'était des fichiers classiques. ● Ecrivain == > write : Exemple : Pour ouvrir l’entrée d'un tube « essai » en écriture : int desc1,n; desc1 = open("essai", O_WRONLY); n= write(desc1 , chaineAEcrire, TAILLE_Ecriture); Ouverture et écriture 34 ● Lecteur == > read : Exemple : Pour ouvrir la sortie d'un tube « essai » en lecture : int desc0,n_lus; desc0 = open("essai", O_RDONLY); n_lus= read(desc0 , chaineALires, TAILLE); ● Destruction : unlink(char *pipe_nomme) unlink("essai");
  35. Écrivez deux programmes indépendants : un écrit un message dans un tube nommé, et l'autre le lit, puis l'affiche. Exemple de communication avec tube nommé 35
  36. Ecrivain : Ecrivain.c (*) int main(){ char buff[255]; char file[]="temp6"; int desc,it; it=mkfifo(file,0755); printf("it=%d n",it); if(it==-1) (*) #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include<string.h> Exemple de communication avec tube nommé if(it==-1) { printf(“erreurn"); exit(1); } printf("saisir n"); fgets(buff,255,stdin); desc=open(file,O_WRONLY); write(desc,buff,strlen(buff)); return 0; } 36
  37. Lecteur : lecteur.c #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include<string.h> Exemple de communication avec tube nommé int main(){ char buff[255]; char file[]="temp6"; int desc, n_lus; desc=open(file,O_RDONLY); while((n_lus= read(desc,buff,30))) printf("recu : %s",buff); unlink(file); return 0; } 37
Anúncio