Pipes: Rohrpost zwischen Prozessen
« | 21 Feb 2019 | »Hätte man “Pipes” in der Programmierung als “Pipeline” bezeichnet, würden vielleicht mehrere Entwickler den Namen kennen und die Technik einsetzen.
Linux bzw. Unix - Fans wissen mit dem Begriff gut umzugehen, doch auf Seiten der Windows Gemeinde fehlt dieses Wissen leider nur all zu oft.
Dabei sind Pipes eine schöne klassische Form Daten zwischen Eltern- und Kindprozessen auszutauschen.
Pipes sind virtuelle Datenschläuche. Was man auf den einen (Schreib-)Seite hineinfallen lässt, kommt auf der anderen (Lese-)Seite wieder heraus. Wird eine Pipe erzeugt, erhalten wir in der Regel zwei Handles bzw. Filedescriptoren, nämlich ein Element zum Hineinschreiben und eines zum Herauslesen.
Wenn man von einem kleinen Puffer mal absieht, blockiert ein Schreibvorgang so lange, bis die Leseseite die Daten gelesen hat. Umgekehrt blockiert ein Lesevorgang so lange, bis mindestens ein Byte auf der anderen Seite geschrieben wurde.
Anonymous vs. Named Pipes
Zu den ältesten und bekanntesten anonymen Pipe-Formen zählen STDIN, STDOUT und STDERR, also die Standard-Ein- und -Ausgabe eines Prozesses. Sie werden bei der Erzeugung eines Prozesses vom Elternprozess entweder explizit vorgegeben oder von diesem einfach geerbt.
Viele Pipes sind anonym, also namenlos. Bei ihrer Erzeugung entstehen die
beiden erwähnten Lese- und Schreib-Handles/Descriptoren, und die Pipe
existiert so lange, wie die Handles darauf offen sind.
Ein Prozess kann ein solches Handle zwar klonen, aber von außen kommt man
nicht (bzw. nur sehr schwer) an sie heran.
Benannte Pipes (Named Pipes) erhalten einen eindeutigen Namen und können explizit von jedem Prozess geöffnet werden, der die nötigen Rechte hat. Damit lässt sich quasi eine kleine lokale Server-Client-Architektur aufsetzen um die Pipe wie einen Server oder Client zu nutzen.
Windows
Windows stellt mit den beiden APIs
CreatePipe()
und CreateNamedPipe()
alles bereit, was man für die Erzeugung beider Formen benötigt.
Zum Öffnen einer bestehenden Named-Pipe dient
CreateFile()
und gelesen bzw. geschrieben wird die Pipe ganz klassisch mit
ReadFile()
und
WriteFile()
.
CreatePipe()
hat leider den Nachteil, dass man damit keine asynchronen
IO-Funktionen (Stichwort: Overlapped)
damit nutzen kann.
Die GATE Implementierung nutzt daher den Umweg, dass eine Named-Pipe mit
einem zufälligen Namen erzeugt wird, denn hier kann man Overlapped-IO
einsetzen.
Windows CE
Windows CE kennt - so weit ich weiß und zu meiner Verwunderung - keine Pipes außer Konsolen (STDIN, STDOUT) auf einigen Plattformen (aber nicht auf allen).
Es gibt eine Ersatztechnologie mit dem Namen “Message-Queues”. Diese können ähnlich wie Pipes genutzt werden, haben aber andere APIs und sind auch keine echten Pipes.
In der früheren GATE-Implementierung hatte ich dieses Verfahren mal ausprobiert, aber nie einen praktischen Nutzen daraus gezogen. Falls ich mal wieder dazu kommen sollte, gibt es sich einen Beitrag dazu.
POSIX, BSD, Linux
Hier wurden Pipes geboren, denn unter Unix Systemen ist es Gang und Gäbe, dass Programme Pipes erzeugen, sie an ihre Kindprozesse weitervererben und so mit ihrem “Nachwuchs” kommunizieren.
pipe()
erzeugt ein Filedescriptor-Paar zum anonymen Lesen und Schreiben,
und die üblichen Verdächtigen
read()
und write()
sorgen für den Zu- bzw. Abfluss von Bytes.
Benannte Pipes werden als “besondere” Dateien im Dateisystem abgelegt. Obwohl
es keine zwingende Vorgabe gibt, wo das sein soll, empfiehlt sich ein
standardisierter Ort, wie /var
oder /tmp
)
mkfifo()
erzeugt diese spezielle FIFO-Pipe-Datei, die mit
open()
entweder zum Lesen oder zum Schreiben separat geöffnet werden kann.
Im Gegensatz zu Windows bleibt eine solche FIFO-Datei auch erhalten, wenn
ihr Erstellerprozess und alle anderen Nutzer beendet wurden.
Man kann bzw muss sie dann per
unlink()
manuell löschen.
Dank der Tatsache, dass alle Deskriptoren per
select()
ansteuerbar sind, lassen sich alle Pipes problemlos mit asynchroner Kommunikation
einsetzen.
Fazit
Nutzt mehr PIPES, liebe Leute.
Wie oft musste ich schon erleben, dass zum Austausch von 5 Einstellungs-Strings
eine Datenbank zwischen Prozessen einprogrammiert wurde, anstatt diese
Infos einfach per Rohrpost zum Nachbarn zu schicken.
Man braucht keine separaten Server für solche Aufgaben, die Megabytes an RAM
fressen und sau-langsam sind.
Pipes schaffen das mit wenigen Kilobytes mit annähernd Lichtgeschwindigkeit!