Outils personnels
Vous êtes ici : Accueil C & C++ Programmation système Les tubes
Se connecter


Mot de passe oublié ?
Nouvel utilisateur ?
 

Les tubes

Par Eric Salice Dernière modification 14:50
Contributeurs : Benjamin Poulain (Ikipou)
GPL

Les tubes sont des mécanismes de communication inter-processus que l'on peut se représenter comme des tuyaux à une extrémité desquels on peut écrire tandis qu'on peut lire à l'autre extrémité.

Il existe 2 types de tubes:

  • les tubes ordinaires
  • les tubes nommés

Un tube ordinaire ne permettra qu'à des processus de la "même famille" (ayant un ancêtre commun qui a créé le tube) de communiquer). Les tubes nommés n'auront pas cette limitation.

Les tubes ordinaires

L'appel système

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

permet de créer un tube ordinaire. Si l'appel réussit (valeur retournée 0, -1 en cas d'échec), filedes[0] est un descripteur ouvert en lecture seule qui est la "sortie" du tube et filedes[1] un descripteur en écriture seule qui est l'entrée du tube.

Les données présentes dans le tube sont stockées en mémoire et pas sur fichier.

Exemple:

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

int main()
{
int tube[2];
char texte[9];
pipe(tube);
write(tube[1], "abcd", 4);
write(tube[1], "efg", 3);
read(tube[0], texte, 8);
write(STDOUT_FILENO, texte, 8);
return EXIT_SUCCESS;
}

Le programme précédent provoque l'affichage du texte abcdefg.

On constate qu'on ne doit pas forcément faire correspondre la taille des lectures avec celle des écritures. Il n'y a aucune option des tubes qui permette de ne lire que les octets correspondant à une opération d'écriture.

La plupart des opérations vues sur les descripteurs de fichiers peuvent s'appliquer aux tubes. Parmi les exceptions, le postionnement avec lseek().

On peut utiliser les descripteurs d'un tube dans les redirections. C'est par ce moyen que le shell réalise la redirection de la sortie d'un processus vers l'entrée d'un autre avec l'opérateur '|'.

Utilisation

On peut utiliser les fonctions de la bibliothèque standard d'E/S en ouvrant un flux sur un descripteur de tube avec fdopen().

Les tubes ont une taille limitée (sur l'architecture PC 4096 octets, valeur de PIPE_BUF dans <limits.h>. Au delà de cette limite l'écriture dans le tube est bloquante.

Un processus disposant d'un descripteur en lecture sur un tube est appelé "lecteur" pour ce tube. Un processus possédant un descripteur en écriture sur un tube est appelé écrivain pour ce tube.

Un processus peut être plusieurs fois lecteur et écrivain s'il a effectué des appels à dup() ou dup2().

La façon précise dont la lecture s'exécute est la suivante:

Quand un processus demande la lecture de n octets dans un tube:

  • Si le tube n'est pas vide et contient m octets, l'opération de lecture lit un nombre d'octets égal au minimum de n et m.
  • Si le tube est vide
    • Si le nombre d'écrivains est nul, l'opération de lecture ne lit aucun octet (read() retourne 0).
    • Sinon, par défaut la lecture est bloquante jusqu'à ce que le tube ne soit plus vide.

Il est possible de rendre une lecture non bloquante via l'appel système fcntl() qui ne sera pas abordé ici.

L'écriture de n octets dans un tube s'effectue de la façon suivante:

  • Si le nombre de lecteurs du tube est nul, le signal SIGPIPE est envoyé au processus tentant d'écrire. Par défaut, celui-ci termine le processus. S'il est ignoré ou capturé, l'écriture échoue et write() retourne l'erreur EPIPE.
  • Sinon:
    • s'il reste n octets libres dans le tube, ils sont écrits et write() retourne n
    • Sinon
      • si n < PIPE_BUF, l'écriture est boquante jusqu'à ce qu'il y ait n octets libres dans le tube auquel cas l'écriture des n octets a lieu de manière atomique.
      • Sinon, write() remplit le tube et le processus est mis en sommeil jusqu'à ce de la place se libère suite à des lectures dans le tube permettant d'écrire les octets suivant

Il est possible de rendre l'écriture non bloquante.

La présence de lecteurs/écrivains pour un tube pouvant bloquer les opérations de lecture ou d'écriture, il est vivement conseillé de fermer l'un des deux descripteurs fournis par pipe() s'il n'est pas utilisé.

Même s'il est possible via un tube d'effectuer une communication bidirectionnelle car chaque processus peut disposer d'un descripteur en lecture et d'un en écriture, il est plus facile d'utiliser deux tubes (un pour chaque "direction"), afin d'éviter de devoir synchroniser les lectures/écritures entre les processus et d'avoir recours à des mécanismes pour déterminer les limites des messages que les processus s'envoient.

La communication via un tube n'est pas limitée à seulement deux processus.

Les tubes nommés

Les tubes ordinaires ne permettent la communication qu'entre processus disposant de descripteurs "pointant" vers ces tubes. Ces descripteurs ne peuvent être acquis que par héritage limitant donc la communication aux processus descendant du créateur du tube.

Les tubes nommés n'ont pas cette limitation dans la mesure où ils sont référencés dans le système de fichier et sont donc accessibles à partir de plusieurs processus de la même façon que des fichiers.

Création

La création d'un tube nommé s'effectue par la fonction de bibliothèque

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

pathname est le chemin du tube dans le système de fichiers et mode correspond aux droits d'accès qui sont les mêmes que pour la création d'un fichier.

La fonction mkfifo() invoque l'appel système mknod() non décrit ici pour créer le fichier correspondant au tube.

La commande

mkfifo -m mode <nom>

permet de créer un tube à partir d'un shell (l'option -m n'est pas obligatoire, elle sert à préciser les droits).

Les fichiers correspondant à des tubes se distinguent par la lettre 'p' (pipe) en début de ligne de la sortie de la commande ls -l.

Ouverture

L'acquisition d'un descripteur sur un tube nommé s'effectue avec la fonction open() comme pour les fichiers. Il est conseillé de n'utiliser que l'accès en lecture seule ou en écriture seule. Le comportement suite à l'ouverture en lecture écriture est indéfini selon la norme Posix.

L'ouverture en lecture/écriture d'un tube sans écrivain/lecteur est bloquante jusqu'à l'ouverture en écriture/lecture du tube.

Ce qui a été dit pour les tubes ordinaires est valable pour les tubes nommés. La lecture/l'écriture dans un tube nommé peut être effectuée via le shell en utilisant une redirection de l'entrée/la sortie standard.

Actions sur le document