Next: Named Pipes
Up: Interprozesskommunikation
Previous: File Locks
  Contents
Pipes stellen die klassische Methode der Interprozesskommunikation
unter Unix dar, da sie sehr mächtig und einfach zu benutzen sind.
Jedem dürfte eine Befehlszeile wie
-
- ls -la | more
bekannt vorkommen. Die Shell startet die beiden Prozesse ls
und more die über eine Pipe miteinander verbunden sind. ls
schreibt seine Ausgabe in die Pipe und more liest sie daraus.
Man kann theoretisch beliebig viele Prozesse auf diese Weise ''aneinanderreihen''.
Mit dem Systemaufruf pipe() erzeugt man eine Pipe.
-
- #include <unistd.h>
int pipe(int filedes[2]);
Es wird ein temporärer Inode erzeugt und der Systemaufruf gibt zwei
Dateideskriptoren zurück. filedes[0] zum Lesen und filedes[1]
zum Schreiben. Auf diese Dateideskriptoren kann man wie auf eine normale
Datei mit den Systemaufrufen read() und write()
zugreifen.
Damit nun die beiden Prozesse ls und more mit einander
kommunizieren können, müssen die beiden Prozesse jeweils die Dateideskriptoren
des Ein-/Ausgabekanals der Pipe auf die Standardausgabe (Dateideskriptor
1) bzw. Standardeingabe (Dateideskriptor 0) kopieren. Dazu bedient
man sich des Systemaufrufs dup2(). Ausgehend von der Shell
passiert also folgendes:
- Die Shell ruft pipe() auf und bekommt zwei Dateideskriptoren
A und B zurück.
- Die Shell führt für die beiden Programme zweimal fork() aus.
- Die Shell schliesst wieder die beiden Dateideskriptoren A und B.
Der erste Kindprozess (für ls) tut folgendes:
- Aufruf von dup2(B,1) um den schreibenden Dateideskriptor
der Pipe auf den der Standardausgabe zu kopieren
- Schliesst die von der Shell geerbten Dateideskriptoren A und B per
close().
- Aufruf von execve() um das Programm /bin/ls auszufühen.
Der zweite Kindprozess (für more) tut analog folgendes:
- Aufruf von dup2(A,0) um den lesenden Dateideskriptor der
Pipe auf den der Standardeingabe zu kopieren.
- Schliesst die von der Shell geerbten Dateideskriptoren A und B per
close().
- Aufruf von execve() um das Programm /bin/more auszufühen.
Dies geschieht alles auf der Ebene der Systemaufrufe. Auf Kernelebene
werden ein VFS inode Objekt und zwei file Objekte
erzeugt (eins zum Lesen, eins zum Schreiben). Der u Eintrag
des inode Objekts verweist auf eine Struktur pipe_inode_info
[include/linux/pipe_fs_i.h]
-
- struct pipe_inode_info {
wait_queue_head_t wait;
char *base;
unsigned int len;
unsigned int start;
unsigned int readers;
unsigned int writers;
unsigned int waiting_readers;
unsigned int waiting_writers;
unsigned int r_counter;
unsigned int w_counter;
};
wait ist eine Warteschlange der Prozesse, die darauf warten
von der Pipe lesen zu können (z.B. weil gerade jemand etwas in die
Pipe schreibt). base enthält die Startadresse der Speicherseite
die für den Datenaustausch angelegt wurde, start den Startoffset
des aktuell benutzen Bereichs der Speicherseite, len dessen
Länge. Desweiteren gibt es noch diverse Zähler zur Synchronisation
des Zugriffs auf diesen Inode von mehreren lesenden oder schreibenden
Prozessen.
Der Zugriff auf eine Pipe kann von Prozessen im Userspace mit den
normalen read() und write() Systemaufrufen passieren.
Auf Kernelebene wird das durch die Funktionen pipe_read()
und pipe_write() [fs/pipe.c] realisiert. Diese kopieren
die Daten vom Userspace in die von der durch pipe_inode_info
verwalteten Speicherseite in den Kernelspace und zurück. Sie kümmern
sich auch um die Synchronisation und Fehlerbehandlung bei den verschiedensten
Kombinationen von blockierenden und nicht-blockierenden Schreib- und
Lesezugriffen.
[include/linux/limits.h] legt als Grösse für eine Pipe 4096 Bytes
fest (PIPE_BUF), d.h. es können maximal 4 kB Daten ''am Stück''
in eine Pipe geschrieben werden. Nach POSIX Standard hat dies atomar
zu erfolgen, d.h. ein Prozess der nur bis zu dieser Anzahl an Bytes
Daten in eine Pipe schreibt, wird nicht unterbrochen und kann sicher
sein, dass die Daten originalgetreu im Speicherbereich der Pipe angekommen
sind.
Obwohl es theoretisch möglich ist, dass mehrere Prozesse den selben
Dateideskriptor der Pipe benutzen, können Daten nur in 1:1 Beziehungen
ausgetauscht werden. D.h. Daten die ein Prozess in die Pipe schreibt
können nur von einem Prozess gelesen werden.
Pipes sind temporär, d.h. sie existieren nur solange einer der beteiligten
Prozesse noch existiert. Möchte man eine Pipe über die Lebensdauer
der Prozesse erhalten, nutzt man so genannte ''Named Pipes''.
Next: Named Pipes
Up: Interprozesskommunikation
Previous: File Locks
  Contents
2002-02-17