Vous êtes sur la page 1sur 9

Plan de ce cours

EPU - Informatique ROB4


Informatique Systme
Les tubes

Serena Ivaldi, Jean-Baptiste Mouret


serena.ivaldi@isir.upmc.fr
Universit Pierre et Marie Curie
Institut des Systmes Intelligents et de Robotique (CNRS UMR 7222)

2012-2013

Introduction
Quelques informations avant de commencer
La communication par tubes
Principe
Les tubes et le shell
Tubes anonymes
Spcificits
Operations sur les tubes anonymes
Mise en oeuvre
Exercice : redirection de lentre
Les tubes nomms
Spcificits
Operations sur les tubes nomms
Mise en oeuvre
Exercice : client-server

7 Bon savoir ...

Rgles de fonctionnement
I

Les cours et les TPs sont obligatoires.

Tout(e) tudiant(e) arrivant en retard pourra se voir refuser laccs la salle de


cours ou la salle de TP.

La fonction man du shell doit, tant que faire se peut, tre utilise avant de poser
une question par email ou en TP.

References
I

Site web de lUE - Sakai : vous trouverez slides et programmes

A. Tannenbaum & A. Woodhull. Operating Systems : design and


implementation. 3rd ed. Prentice Hall, 2006.

**** example.c ****

Imprimez le code des examples pour suivre le commentaire pendant le cours. Le code
est souvent trop long pour tre affich sur les slides.

La communication par tubes - Pipes & FIFOs

Introduction
I

Chaque processus dispose dun espace dadressage propre et indpendant, protg


des autres processus.

Les processus peuvent avoir besoin de communiquer entre eux pour changer des
donnes.
Linux offre diffrents outils de communication aux processus utilisateurs :

les tubes anonymes (pipes) ou nomms ; les files de messages (message queues) ; la
mmoire partage.

Les tubes sont grs par le systme de gestion de fichiers (cf. cours 4).

Les files de message et la mmoire partage appartiennent la famille des IPC


(Inter Processus Communication).

IPC - Inter process communication


I

Signals (cf. cours 7)

Fork (cf. cours 2)

Semaphores, mutexes, shared memory (cf. cours 3)

Pipes & Fifos

Sockets, messages

Gestion des donnes dans le tube

La gestion des donnes dans le tube est lie au mode daccs squentiel.

Gestion en flots doctets : pas de prservation de la structure du message


dorigine.

Le lecteur reoit dabord les donnes les plus anciennes (FIFO).

Les lectures sont destructives : les donnes lues disparaissent du tube.

Un tube a une capacit finie qui est celle du tampon qui lui est allou.

Cette capacit est dfinie par la constante symbolique PIPE_BUF dfinie dans
<limits .h> .

Un tube peut donc tre plein et de fait amener les processus crivains
sendormir en attendant de pouvoir raliser leur criture : opration bloquante.

Principe
I

Un tube est une tuyau dans lequel un processus peut crire des donnes quun
autre processus peut lire.

La communication dans un tube est unidirectionnelle et une fois choisie ne peut


tre change.

Un processus ne peut donc tre la fois crivain et lecteur dun mme tube.

Les tubes sont grs au niveau du systme de gestion de fichiers et correspondent


des fichiers au sein de ce dernier.

Lors de la cration dun tube, deux descripteurs sont crs, permettant


respectivement de lire et dcrire dans le tube.

Descripteurs accs squentiel.

Accs squentiel : rappel du cours 4


I

les enregistrements sont traits dans lordre o ils se trouvent dans le fichier
(octet par octet) ;

une opration de lecture dlivre lenregistrement courant et se positionne sur le


suivant ;

une opration dcriture place le nouvel enregistrement en fin de fichier ;

mode daccs simple, pas forcment pratique, fichier accessible en lecture seule ou
en criture seule.

Les tubes et le shell : principe

Les tubes de communication peuvent tre utiliss au niveau de linterprteur de


commande shell pour transmettre le rsultat dune commande une autre qui
interprte alors les donnes correspondantes comme donnes dentre.

Un tube shell est symbolis par le caractre | (alt Gr + 6).

Par exemple, il nexiste pas de fonction shell permettant dobtenir directement le


nombre de fichiers dans un rpertoire.

Cette information peut tre obtenue en comptant le nombre de lignes retourn


par ls l.

I
I

La fonction shell wc l permet de retourner le nombre de lignes dun fichier.

Il suffit donc denvoyer la sortie de ls l vers lentre de wc l. Ceci peut tre fait
avec un pipe : ls l | wc l.

Pipes & FIFOs


Pipe

The basic mechanism of IPC

Read/write : data written to the pipe by one process can be read by another
process

Data handled in FIFO order (first-in first-out)

A pipe has no name : to use it, both ends (processes) must inherit it from the
single process that created the pipe

Anonymous, temporary connection

Ex : redirection for better reading large outputs


I
I
I

dmesg
dmesg | less
dmesg | grep disabled | grep IO

Ex : redirection to isolate items of interest

$ ls -l | grep Makefile
-rw-rw-r-- 1 icub icub 306 Sep 19 18:22 Makefile
$ dmesg | grep nouveaufb
[
17.959472] fb1: nouveaufb frame buffer device

Fifo
I

A fifo is a named pipe

Similar to a pipe in its purpose, but substantially different, since it is a special file

Its name is in fact a path within the file system

Any process can open a fifo for reading/writing in the same way as an ordinary
file (assuming file permissions allow it)

Pipes & FIFOs


Similarities :
I

Both are used to read/write datas (one-way flow of data)

Once they are opened (different way !) the semantics fro read/write is the same

Both have to be open at both ends simultaneously

Reading from a pipe/fifo where nobody writes returns EOF

Writing to a pipe/fifo without reader fails (SIGPIPE signal and error EPIPE are
generated)

The communication channel is a byte stream

File positioning (lseek) is not allowed !

Read/write operations are sequential and in fifo (first-in-first-out) order

Differences :
Name
Creation
Access
Reading
Writing

pipe
no (anonymous)
pipe
through file descriptors
file descriptor [0]
file descriptor [1]

fifo
yes (path)
mkfifo
through path in file system
O_RDONLY
O_WRONLY

Pipes (tubes anonymes)

Spcificits de tubes anonymes

Cration
I

Un tube anonyme est cr par appel la fonction pipe() dont les prototype est
int pipe ( int desc [2]); (dfini dans #include <unistd.h> ).

pipe() retourne deux descripteurs placs dans desc .

desc [0] : descripteur en lecture.

desc [1] : descripteur en criture.

Les tubes anonymes sont grs au niveau du systme de gestion de fichiers et


correspondent des fichiers au sein de ce dernier.

Les deux descripteurs sont respectivement associs un fichier ouvert en lecture


et un fichier ouvert en criture dans la table des fichiers ouverts.

Un fichier physique est associ au tube mais aucun bloc de donnes ne lui
correspond.

Les donnes transitant dans un tube sont places dans un tampon allou dans la
mmoire centrale.

Tout processus ayant connaissance du descripteur en lecture desc [0] dun tube
peut lire dans ce dernier (on peut donc avoir plusieurs processus lecteurs dans un
mme tube).

Tout processus ayant connaissance du descripteur en criture desc [1] dun tube
peut crire dans ce dernier (on peut donc avoir plusieurs processus crivains dans
un mme tube).

En cas dchec, pipe() renvoie -1 (0 sinon) et errno contient le code derreur et


le message associ peut tre rcupr via perror () .
3 erreurs possibles :

Ce fichiers ont la particularit dtre sans nom.

Le lecteur reoit dabord les donnes les plus anciennes (FIFO).

Cette absence de nom induit que ce type de tube ne peut tre manipul que par
des processus ayant connaissance des deux descripteurs (lecture/criture) associs
au tube.

Ce sont donc le processus crateur du tube et ses descendants qui prennent


connaissance des descripteurs du tube par hritage des donnes de leur pre.

I
I
I

EFAULT : le tableau desc pass en paramtre nest pas valide.


EMFILE : le nombre maximal de fichiers ouverts par le processus a t atteint.
ENFILE : le nombre maximal de fichiers ouverts par le systme a t atteint.

Lecture
I

La lecture dans un tube anonyme seffectue en mode binaire par le biais de la


fonction read() dont le prototype est rappel ici :
Cette fonction permet la lecture de count caractres (octets) depuis le tube dont
le descripteur en lecture est fd qui sont placs ladresse buf .
Elle retourne en rsultat le nombre de caractres rellement lus :

Fermeture
I

Un tube anonyme est considr comme ferm lorsque tous les descripteurs en
lecture et en criture existants sur ce tube sont ferms.

Un processus ferme un descripteur de tube fd en utilisant la fonction


int close ( int fd ); .

A un instant donn, le nombre de descripteurs ouverts en lecture dtermine le


nombre de lecteurs existants du tube. idem pour le nombre dcrivains.

Un descripteur ferm ne permet plus daccder au tube et ne peut pas tre


rgnr.

ssize_t read( int fd , void buf, size_t count);

I
I
I

si le tube nest pas vide et contient taille caractres, read extrait du tube
min(taille, count) caractres qui sont lus et placs ladresse buf ;
si le tube est vide et que le nombre dcrivains est non nul, la lecture est bloquante et le
processus lecteur est mis en sommeil jusqu ce que le tube ne soit plus vide ;
si le tube est vide et que le nombre dcrivains est nul, la fin du fichier est atteinte et le
nombre de caractres rendu est nul.

Lopration de lecture peut tre rendue non bloquante par un appel la fonction
de manipulation des descripteurs de fichier fcntl () :
fcntl (desc [0], F_SETFL, O_NONBLOCK); .

Dans ce cas, le retour est immdiat si le tube est vide.

Recap
Ecriture
I

I
I

Lcriture dans un tube anonyme seffectue en mode binaire par le biais de la


fonction write () dont le prototype est rappel ici :
Cette fonction permet lcriture de count caractres (octets) placs ladresse
buf dans le tube dont le descripteur en criture est fd .
Elle retourne en rsultat le nombre de caractres rellement crits :
I
I
I

#include <unistd.h>
int pipe(int filedes[2]);
int close(int fd);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

ssize_t write ( int fd , const void buf, size_t count);

si le nombre de lecteurs dans le tube est nul alors une erreur est gnre et le signal
SIGPIPE est dlivr au processus crivain qui se termine.
si le nombre de lecteurs dans le tube est non nul, lopration dcriture est bloquante
jusqu ce que count caractres aient effectivement t crits dans le tube ;
Dans le cas o le nombre de caractres count crire dans le tube est < la constante
symbolique PIPE_BUF (4096 octets), lcriture des count caractres est atomique :
les count caractres sont tous crits les uns la suite des autres dans le tube.
Dans le cas o le nombre de caractres count crire dans le tube est > la
constante symbolique PIPE_BUF , lcriture des count caractres peut tre
arbitrairement dcoupe par le systme.

filedes[0] is for reading

filedes[1] is for writing

Creation : on success, 0 is returned. On error, -1 is returned (see errno for more)


We typically fork after creating a pipe, because

1. there is no reason to use a IPC primitive such as a pipe in a single process


2. there is no way for any other process to access the pipe other than through the file
descriptors copied in a fork

Lopration dcriture peut elle aussi tre rendue non bloquante.

Mise en oeuvre
Cas typique
I

Le processus pre ouvre un tube en utilisant la fonction pipe() ;

Le processus pre fork () (cration dun fils qui connat donc les descripteurs du
tube) ;

Les descripteurs en lecture et criture sont utilisables par les deux processus.
Chacun ferme le descripteur qui lui est inutile : par exemple le pre ferme le
descripteur en criture et le fils le descripteur en lecture.

Le fils envoie un message son pre.

le pre lit le message.

Lorsquun tube est cr, le processus associ cette cration est potentiellement
crivain ou lecteur.

La logique veut donc quon ne se dclare pas lecteur ou crivain mais plutt
quon ferme avec close () le descripteur correspondant au mode de
fonctionnement qui ne nous intresse pas.

La cration dune communication bi-directionnelle entre deux processus ncessite


lutilisation de deux tubes.

**** pipefork.c ****


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# include
# include
# include
# include
# include
# include

< unistd .h >


< stdlib .h >
< sys / types .h >
< stdio .h >
< sys / wait .h >
< time .h >

int main ( int argc , char ** argv )


{
int fd [2];
int i ;
if ( pipe ( fd ) )
/* create pipe
*/
fprintf ( stderr , " pipe error \ n " ) ;
else if ( fork () )
{
/* parent : the reader */
close ( fd [1]) ;
/* close the write pipe */
read ( fd [0] , &i , sizeof ( int ) ) ;
/* read an integer
*/
printf ( " Father : reading the value % d \ n " , i ) ;
}
else
{
/* child : the writer
*/
close ( fd [0]) ;
srand ( time ( NULL ) ) ;
i = rand () ;
/* g e n e r a t e s integer */
printf ( " Child : writing the value % d \ n " , i ) ;
write ( fd [1] , &i , sizeof ( int ) ) ;
/* writes the integer */
}
}

return 0;

TEST
**** pipefork.c ****
Output :
icub@eva:~/progsys/cours/c5/code$ ./pipe1
Child: writing the value 1786065064
Father: reading the value 1786065064

Write a program that creates a pipe, then forks to create a child. After the fork each
process closes the descriptors that it does not need.
I

The father process writes the string received as command-line parameter in the
pipe (argv[1])

The child reads the string (note ! byte per byte !) from the pipe and print it on
stdout

Example :
$ ./test1 "luke i am your father"
luke i am your father

FIFOs (tubes nomms)

Les tubes nomms sont galement grs par le systme de gestion de fichiers.

Le fichier associ possde un nom et le tube est donc accessible par tout
processus connaissant ce nom et disposant des droits daccs au tube.

Les tubes nomms permettent donc des processus sans lien de parent de
communiquer selon un mode squentiel.

De manire similaire au tube anonyme, un fichier physique est associ au tube


nomm mais aucun bloc de donnes ne lui correspond.

Les donnes transitant dans un tube nomm sont donc places dans un tampon
allou dans la mmoire centrale.

Note : FIFO special files are indicated by ls l with the file type p
icub@eva:/tmp/fifo$ ls
icub@eva:/tmp/fifo$ mkfifo serefifo
icub@eva:/tmp/fifo$ ls -l
total 0
prw-rw-r-- 1 icub icub 0 Sep 26 15:15 serefifo

Cration
I

Un tube nomm est cr par lintermdiaire de la fonction mkfifo () dont le


prototype est donn par :
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char pathname, mode_t mode);

Lecture / criture

pathname correspond au chemin daccs dans larborescence de fichiers pour le

tube.

mode permet de spcifier les droits daccs associs au tube (cf. cours 4).

mkfifo retourne 0 en cas de succs et -1 dans le cas contraire (EEXIST error if


the FIFO already exists)

Fermeture
I

Ouverture
I

Louverture dun tube nomm se fait par lintermdiaire de la fonction open()


tudie au cours 4.

Le processus effectuant louverture doit possder les droits correspondants sur le


tube.

open() renvoie un descripteur correspondant au mode douverture spcifi.

Par dfaut, la fonction open() applique un tube est bloquante. Ainsi, la


demande douverture en lecture est bloquante tant quil nexiste pas dcrivain
sur le tube.

De manire similaire, la demande douverture en criture est bloquante tant quil


nexiste pas de lecteur sur le tube.

Ce mcanisme permet deux processus de se synchroniser et dtablir un RDV en


un point particulier de leur excution.

La lecture et lcriture dans un tube nomm seffectue en utilisant les fonctions


read et write (cf. tubes anonymes).

La fermeture se fait en utilisant close () .

Destruction
I

La destruction se fait en utilisant unlink () dont le prototype est donn par :


int unlink (const char pathname);

Recap

**** fifocs.h ****


#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int mkfifo(const char *pathname, mode_t mode);
int open(const char *pathname, int flags);
int close(int fd);
int unlink(const char *pathname);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

I
I

The path identifies the FIFO


There is no need to fork : if the path is known, different programs can access the
FIFO
I

7 see next example on client/server !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# include
# include
# include
# include
# include
# include
# include
# include

< unistd .h >


< stdlib .h >
< sys / types .h >
< sys / stat .h >
< sys / wait .h >
< sys / errno .h >
< stdio .h >
< time .h >

extern int errno ;


# define FIFO1 " / tmp / fifo1 . txt "
# define FIFO2 " / tmp / fifo2 . txt "
# define PERMISSIONS 0666

**** fifo_server.c ****


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

**** fifo_client.c ****


// use FIFO ( not pipes ) to i m p l e m e n t a simple client - server model
// the server
# include " fifocs . h "
int main ( int argc , char ** argv )
{
int readfd , writefd ;
if ( ( mkfifo ( FIFO1 , PERMISSIONS ) <0) && ( errno != EEXIST ) )
{
printf ( " server : can t create FIFO1 to read : % s \ n " , FIFO1 ) ; return 1;
}
if ( ( mkfifo ( FIFO2 , PERMISSIONS ) <0) && ( errno != EEXIST ) )
{
unlink ( FIFO1 ) ;
printf ( " server : can t create FIFO2 to write : % s \ n " , FIFO2 ) ; return 1;
}
if ( ( readfd = open ( FIFO1 , 0) ) < 0)
{ printf ( " server : can t open FIFO1 to read \ n " ) ; return 1; }
if ( ( writefd = open ( FIFO2 , 1) ) < 0)
{ printf ( " server : can t open FIFO2 to write \ n " ) ; return 1; }
/* server ( readfd , writefd ) ; */
// just to give the time to the client to test the fifos
sleep (1000) ;
close ( readfd ) ;
close ( writefd ) ;
return 0;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// use FIFO ( not pipes ) to i m p l e m e n t a simple client - server model


// the client
# include " fifocs . h "
int main ( int argc , char ** argv )
{
int readfd , writefd ;
// open the fifos : assume the server already created them
if ( ( writefd = open ( FIFO1 , 1) ) < 0)
{ printf ( " client : can t open FIFO1 to write \ n " ) ; return 1; }
if ( ( readfd = open ( FIFO2 , 0) ) < 0)
{ printf ( " client : can t open FIFO2 to read \ n " ) ; return 1; }
/* client ( readfd , writefd ) ; */
close ( readfd ) ;
close ( writefd ) ;
// now delete the fifos since we re finis hed
if ( unlink ( FIFO1 ) < 0)
{ printf ( " client : can t unlink FIFO1 " ) ; return 1; }
if ( unlink ( FIFO2 ) < 0)
{ printf ( " client : can t unlink FIFO2 " ) ; return 1; }
return 0;

TEST
I

Since the server creates the fifos, once we launch the server :
$ ./fifo_server
we can see the fifos by checking their path :
icub@eva:/tmp$ ls -l
prw-rw-r-- 1 icub
icub
prw-rw-r-- 1 icub
icub

0 Sep 26 16:00 fifo1.txt


0 Sep 26 16:00 fifo2.txt

Note that in this example, the client assumes the fifos have already been created.
This is what happens if we launch the client first :
$ ./fifo_client
client: cant open FIFO1 to write

Beware ! If we accidentally press CTRL+C on the server, fifos are not destroyed
properly... check /tmp/ to see if they were closed properly.

Modify the client-server example so that client and server do really something, e.g.
exchange data :
I

the client connects to the server, sends an integer, and waits for the server to
reply with another integer

the server wait for a client connection, receives an integer, perform some
processing on this integer (for example, add +10), then send the result to the
client

The server :
$ ./fifo_server
server: wait for incoming connection
server: read 42, sent 52
The client :
$ ./fifo_client
client: sent 42
client: wait for server reply
client: read 52

Questions ?