ASR31-valarche-2021/ASR31-6-Tubes.md

6.0 KiB
Raw Blame History

La communication via les tubes

tubes sans nom

Liaison unidirectionnelle. La taille maximale est approximativement de 4Ko. Ils existent via le shell et lappel système pipe(). Lecture FIFO.

#include <unistd.h>
int pipe(int desc[2]); /* ou int *tubEDesc */

Cela correspond à la création dun nouveau tube anonyme. En cas de réussite, il y aura location dun noted sur le disque des tubes, de deux entrées dans la tables des fichiers ouverts (une en lecture et une en écriture et pour chacune des entrées dun descripteur dans la table des descripteurs du processus appelant:

  • desc[0] correspond au descripteur en lecture sur le tube
  • desc[1] correspond au descripteur en écriture sur le tube Un appel réussi renvoie 0, si échec alors -1 (et la variable errno est positionnée (EMFILE table des descripteurs du proc. pleine ou ENFILE table des fic ouverts du sys. pleine).

Les tubes anonymes ne sont pas des « fichiers » comme les autres les appels open, stat, access, link, unlink, rename, chmod, chown et lseek ne sont pas autorisés. On peut accéder à des informations (et les modifier) via les appels : fstat et fcntl.

Le processus usuel pour la communication inter-processus : 1- le processus père créé un tube sans nom avec pipe() 2- le processus père créé les fils 3- les processus écrivains ferment l'accès en lecture du tube 4- les processus lesteurs ferment l'accès en écriture du tube 5- communication via read et write 6- fermeture des tubes quand plus besoin de communiquer

#include <sys/types.h> 
#include <unistd.h>  
#include <stdio.h> 
#include <string.h>

#define R 0 
#define W 1

int main ( ) {
	int fd[2] ;
	char message[100] ;
	int nboctets ;
	char *phrase = " message envoye au pere par le fils " ;
	
	pipe ( fd ) ;
	if (fork() == 0) {
		close ( fd[R] ) ;
		write ( fd[W] , phrase , strlen (phrase) + 1 ) ;
		close ( fd[W] ) ;
	}
	else {
		close ( fd[W] ) ;
		nboctets = read ( fd[R] , message , 100 ) ;
		printf ( " Lecture %d octets : %s\n" , nboctets , message ) ;
		close ( fd [R] ) ;
	}
	return 0;
}

Si on veut structurer les échanges, on a intérêt à prévoir un protocole de communication.

Comment un shell exécute une commande comme ls | wc. Dans un premier temps, le tube est créé avec la commande pipe(). Puis un processus fils est créé. Ce processus fils héritera des accès en entrée et en sortie du tube. Ensuite, le processus parent ferme son accès à la sortie du tube et fait pointer STDOUT sur lentrée du tube. Dans le même temps, le processus fils ferme son accès à lentrée du tube et redirige STDIN sur la sortie du tube. Finalement, les deux processus sont remplacés par les programmes ls et wc.

On peut faire de la communication bi-directionnelle en utilisant 2 tubes.

L'algo de lecture pour read(p, buf, TAILLE_BUF):

if tube(p) != vide then 
	let taille = size(p) in
	buf = extract(p, min(taille, TAILLE_BUF))
else
	if nb(writer, p) == 0 then
		return 0
	else 
		if read is Bloquant then
			process = wait 
		else 
			return -1 
			errno = EAGAIN (* O_NONBLOCK *)

Attention à l'interblocage

Pour le cas du write(p, buffer, tailleBuf). On a la garantie que si la taille à écrire est inférieure à la PIPE_BUF alors l'écriture est atomique. Si ce n'est pas le cas, c'est le système qui décide...

if nb(lecteur(p) == 0) then 
	receive(SIGPIPE) (* si signal capté => return -1 et errno = EPIPE *)
else 
	if write is bloquant then 
		wait until all is written
	else 
		if n > PIPE_BUF then return m (* m < n *)
		if n  PIPE_BUF and freesize(p) > n then GOOD
		if n  PIPE_BUF and freesize(p) < n then return -1

On peut utiliser dup et dup2 pour dupliquer. Cela permet de rediriger les E/S standards sur un tube.

A faire en exercice de TP

Avec exemple :

  • écriture dans un tube sans lecteur
  • ecriture dns un tube plein

tubes nommés

Il a été rajouté un autre type de tube qui facilite un peu les choses en rajoutant les fonctionnalités suivantes :

  • Ils ont chacun un nom qui existe dans le système de fichiers (une entrée dans la table des fichiers).
  • Ils sont considérés comme des fichiers spéciaux.
  • Ils peuvent être utilisés par des processus indépendants, à condition quils sexécutent sur une même machine.
  • Ils existeront jusquà ce quils soient supprimés explicitement.
  • Ils sont de capacité plus grande, denviron 40 Ko.

Ainsi, deux processus sans relation parent-enfant peuvent échanger des données au moyen des tubes nommés.

SYNOPSIS
     #include <sys/types.h>
     #include <sys/stat.h>

     int
     mkfifo(const char *path, mode_t mode);

DESCRIPTION
     mkfifo() creates a new fifo file with name path.  The access permissions are
     specified by mode and restricted by the umask(2) of the calling process.

     The fifo's owner ID is set to the process's effective user ID.  The fifo's
     group ID is set to that of the parent directory in which it is created.

RETURN VALUES
     A 0 return value indicates success.  A -1 return value indicates an error,
     and an error code is stored in errno.

On peut créer des tubes depuis le shell makefifo my_tube. On obtient alors :

bash-4.4$ ls -l my_tube 
prw-r--r--  1 pvalarcher  staff  0  9 oct 05:48 my_tube
bash-4.4$ 

TD

Complétez le programme suivant afin de rendre la communication bidirectionnelle entre 2 processus sans laisser de zombies. Le père dort 2 secondes après avoir envoyé son message au fils et le fils dort 4 secondes après avoir envoyé son message au père. On na pas besoin de variables additionnelles.

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define R 0
#define W 1

int main() {
	int fdp[2], // père
		fdf[2]; // fils
	
		char message[100]; // pour récupérer un message

		char *phrasef = "message envoyé au père par le fils";

		char *phrasep = "message envoyé au fils par le père";
// À COMPLÉTER !!

return 0;
}