ein Kapitel zurück                                           ein Kapitel weiter

Pipes als auch FIFO sind die einzigen beiden IPC die garantiert auf jedem System vorhanden sind. Also sowohl POSIX, SVR4 und BSD - Konform.

Eine Pipe ist ein undirektionaler Kommunikationskanal zwischen zwei verwandten Prozessen. Sie haben Pipes schon des öfteren verwendet. Beispielsweise......

ps x | less

Hiermit haben wir in der Shell 2 Prozesse gestartet. Ein Prozeß führt ps x aus und schreibt seine Ausgabe an die Standardausgabe. Durch die Pipe '|' wird diese Standardausgabe an den Prozeß less gesendet. Dieser ließt von seiner Standardeingabe, in unserem Fall von ps x, ein.

Eine Pipe dient außer der Kommunikation zwischen 2 Prozessen hervoragend zu Flußkontrolle. Dies daher weil ein Pipe nur ein bestimmte Menge an Daten aufnehmen kann (normalerweise 4KByte). Ist die Pipe voll, wird ein Prozeß mindestens solange angehalten, bis mindestens 1 Byte aus der vollen Pipe gelesen wird und die wieder Platz für diese wieder mit Daten befüllt werden kann. Andersherum, das selbe Bild, ist die Pipe leer wird der lesende Prozeß solange angehalten, bis der Schreibende Prozeß etwas in diese Pipe schickt.

Sie konnten sich nun schon ein Bild zu den Pipes machen. Es gibt also ein Schreibseite und eine Leseseite. Es ist als nur eine Kommunikation in einer Richtung möglich (Halbduplex). Sie können sie unsere Beispiel oben so vorstellen.........




Anhand dieses Beispiels können sie sehen wie die Standardausgabe des Aufrufs ps x durch ein Pipe an less weitergeleitet wird. less verfährt dann weiter wie gewohnt.

Dies stellt also das Prinzip einer Pipe da. Folglich können sie auch an der Pipe erkennen, das diese 2 Diskriptoren besitzt. Der erste Deskriptor dient zum lesen und der zweite Dekriptor dient dazu in die Pipe zu schreiben.

Folglich sieht der Syntax wie wir eine Pipe in C erzeugen können folgendermaßen aus....

#include <unistd.h>

int pipe(int fd[2]);  

Also öffne wir mit Pipe zwei Filedeskriptoren.....

fd[0] ist der Filedeskriptor zum Lesen
fd[1] ist der Filedeskriptor zum Schreiben in die Pipe  

Auf unser Beispiel in der Shell macht dies folgendes Bild aus........




Nun hiermit können aber immer noch nicht 2 Prozesse miteinander kommunizieren. Es fehlt also noch ein Rückleitung. Und die erstellen wir mit einem fork-Aufruf.

Nun haben wir zwei Prozesse. Der Kindprozeß erbt nun von seinem Elternprozeß beide offenen Filedeskriptoren. Um nun den Prozessen klar zu machen, wer wohin schreibt und wer was liest, wird durch geschicktes Öffnen und Schließen der Standardkanäle bestimmt, wer aus der Pipe liest und wer etwas reinschreibt. Dabei habe sie 2 Möglichkeiten.......

  • Der Elternprozeß ließt aus dem Kindprozeß und schreibt in die Pipe. Somit wird die Schreibseite (fd[1]) des Elternprozeß geschlossen. Die Leseseite (fd[0]) des Kindprozesses wird ebenso geschlossen.
  • Der Kindprozeß ließt aus dem Elternprozeß und schreibt in die Pipe. Damit geschieht dasselben wie eben erklärt nur umgekehrt.

Sollte einer der Prozesse in einer Pipe Schreiben obwohl er für das Lesen bestimmt war wird das Signal SIGPIPE ausgelöst. Dies sollten sie Abfangen, ansonsten wird der Prozeß beendet.

Somit könnte sich folgendes Bild ergeben......




Nun wollen wir uns auch mal ein Beispiel ansehen, wie das ganze in der Praxis funtkioniert........

/*Download:pip1.c*/
#include <unistd.h> #include <sys/wait.h> #include <stdio.h> #include <sys/types.h> #include <fcntl.h> #define USAGE printf("usage : %s Datei\n",argv[0]); #define MAX 4096 int main(int argc, char *argv[]) { int fd[2], fd1,i, n; pid_t pid; char puffer[MAX]; FILE *datenzeiger; if(argc !=2) { USAGE; exit(0); } if((fd1=open(argv[1], O_RDONLY)) < 0) { perror("open : "); exit(0); } /*Wir erstellen eine pipe*/ if(pipe(fd) <0) { perror("pipe : "); exit(0); } /*Wir erzeugen einen neuen Prozess*/ if((pid=fork()) < 0) { perror("pipe : "); exit(0); } else if(pid > 0) /*Elternprozess*/ { close(fd[0]); /*Leseseite schließen*/ n=read(fd1, puffer, MAX); if((write(fd[1], puffer, n)) != n) { perror(" write : "); exit(0); } if((waitpid(pid, NULL, 0)) < 0) { perror("waitpid : "); exit(0); } } else /*Kindprozess*/ { close(fd[1]); /*Schreibseite schließen*/ n=read(fd[0], puffer, MAX); if((write(STDOUT_FILENO, puffer, n)) != n) { perror(" write : "); exit(0); } } exit(0); }

Hier erzeugen wir eine Pipe und schließen die Leseseite der Eltern und Schreibseite des Kindes. Das Kind ließt eine Datei, die sie mit als Argument in der Kommandozeile angeben und der Elternprozeß gibt diesen auf die Standardausgabe aus. Da es auch einen anderen Stil gibt dies durchzuführen, will ich ihn diesen nicht vorenthalten.....

Beispielsweise im Elternprozeß....

w=dup(fd[1]); /*Duplizieren von Schreibseite*/
close(fd[0]); close(fd[1]); /*Schließen beider Deskriptoren*/
n=read(fd1,puffer,MAX);
write(w,puffer,n); /*Schreiben in die Pipe*/  

Durch das duplizieren des Filedeskriptors in diesem Beispiel bewirkt nur das sie denn Orginaldeskriptor schließen und mit der Kopie weiterarbeiten. Ansonsten stellt dies keine Verbesserung oder Verschlechterung da. Diese Möglichkeit wird häufig und gerne eingesetzt in dem man 2 Pipes erzeugt und beide Prozesse sowohl lesen als auch schreiben können. Wir kommen anschließend noch dazu.

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf