next up previous contents
Next: Named Pipes Up: Interprozesskommunikation Previous: File Locks   Contents

Pipes

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:

  1. Die Shell ruft pipe() auf und bekommt zwei Dateideskriptoren A und B zurück.
  2. Die Shell führt für die beiden Programme zweimal fork() aus.
  3. Die Shell schliesst wieder die beiden Dateideskriptoren A und B.
Der erste Kindprozess (für ls) tut folgendes:

  1. Aufruf von dup2(B,1) um den schreibenden Dateideskriptor der Pipe auf den der Standardausgabe zu kopieren
  2. Schliesst die von der Shell geerbten Dateideskriptoren A und B per close().
  3. Aufruf von execve() um das Programm /bin/ls auszufühen.
Der zweite Kindprozess (für more) tut analog folgendes:

  1. Aufruf von dup2(A,0) um den lesenden Dateideskriptor der Pipe auf den der Standardeingabe zu kopieren.
  2. Schliesst die von der Shell geerbten Dateideskriptoren A und B per close().
  3. 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 up previous contents
Next: Named Pipes Up: Interprozesskommunikation Previous: File Locks   Contents
2002-02-17