Grundlegendes zu Pipe-Befehlen unter Unix / Linux


16

Ich habe zwei einfache Programme: Aund B. Awürde zuerst laufen, Berhält dann das "stdout" von Aund verwendet es als "stdin". Angenommen, ich verwende ein GNU / Linux-Betriebssystem und der einfachste Weg, dies zu tun, wäre:

./A | ./B

Wenn ich diesen Befehl beschreiben müsste, würde ich sagen, dass es sich um einen Befehl handelt, der Eingaben (dh Lesevorgänge) von einem Produzenten ( A) entgegennimmt und an einen Verbraucher ( B) schreibt . Ist das eine korrekte Beschreibung? Vermisse ich etwas?



Es ist kein Befehl, sondern ein vom bash-Prozess erstelltes kenerl-Objekt, das als Standard von Prozess A und als Standard von B verwendet wird. Zwei Prozesse werden fast gleichzeitig gestartet.
炸鱼 0: 德里克

1
@ 炸鱼 Sie haben Recht - Kernel-Pipeline ist ein Objekt im Pipefs-Dateisystem, aber was die Shell anbelangt - technisch gesehen ist das ein Pipeline-Befehl
Sergiy Kolodyazhnyy

Antworten:


26

Das einzige, was an Ihrer Frage als falsch auffällt, ist, dass Sie sagen

A würde zuerst laufen, dann bekommt B die Standardabweichung von A

Tatsächlich würden beide Programme fast zur gleichen Zeit gestartet. Wenn es keine Eingabe für Bden Leseversuch gibt, wird es blockiert, bis es eine Eingabe zum Lesen gibt. Wenn niemand die Ausgabe liest A, werden die Schreibvorgänge blockiert, bis die Ausgabe gelesen wurde (ein Teil wird von der Pipe gepuffert).

Das einzige, was die an einer Pipeline beteiligten Prozesse synchronisiert, ist die E / A, dh das Lesen und Schreiben über die Pipeline. Geschieht kein Schreiben oder Lesen, laufen die beiden Prozesse völlig unabhängig voneinander ab. Wenn einer das Lesen oder Schreiben des anderen ignoriert, wird der ignorierte Prozess blockiert und schließlich durch ein SIGPIPESignal abgebrochen (wenn geschrieben wird) oder eine Dateiendebedingung in seinem Standardeingabestream abgerufen (wenn gelesen wird), wenn der andere Prozess beendet wird .

Die idiomatische Art zu beschreiben A | Bist, dass es sich um eine Pipeline handelt, die zwei Programme enthält. Die Ausgabe, die auf der Standardausgabe des ersten Programms erzeugt wird, kann auf der Standardeingabe von der zweiten gelesen werden ("[die Ausgabe von] Awird in [die Eingabe von] geleitet B"). Die Shell übernimmt die erforderlichen Installationsarbeiten, um dies zu ermöglichen.

Wenn Sie die Wörter "Verbraucher" und "Erzeuger" verwenden möchten, ist das vermutlich auch in Ordnung.

Die Tatsache, dass dies in C geschriebene Programme sind, ist nicht relevant. Die Tatsache, dass es sich um Linux, MacOS, OpenBSD oder AIX handelt, spielt keine Rolle.


2
Das Schreiben in eine temporäre Datei wurde in DOS verwendet, da dies nicht mehrere Prozesse unterstützte.
CSM

2
@AlexVong Beachten Sie jedoch, dass Ihr Beispiel mit einer temporären Datei nicht genau gleichwertig ist. Ein Programm sucht möglicherweise nach dem Inhalt einer Datei, aber Daten, die aus einer Pipe stammen, können nicht gesucht werden. Ein besseres Beispiel wäre, eine Named mkfifoPipe zu erstellen, dann B im Hintergrund zu starten und A in die Pipe zu schreiben. Dies ist jedoch eine Fehlentscheidung, da der Effekt derselbe wäre.
Kusalananda

2
@AlexVong Die in diesem Artikel gemachten Vereinfachungen trennen ihn von echten Pipelines. Die parallele Ausführung ist wirklich semantisch, keine Optimierung. Es ist eine vernünftige Erklärung der monadischen Bewertung oder Komposition für jemanden, der Shell-Pipelines gesehen hat, aber in der anderen Richtung nicht gültig. Die fifo-Version von Kusalananda ist näher, aber die Teile der Fehlerausbreitung des Modells sind wirklich wichtig und nicht reproduzierbar. (Alles was ich als jemand sage, der sehr auf der "Shell Pipelines sind nur Funktionskomposition" trainiert)
Michael Homer

6
@ AlexVong Nein, das ist völlig aus der Bahn. Das kann nicht einmal so etwas Einfaches erklären yes | sed 10q
Onkel Billy

1
@UncleBilly Ich stimme Ihrem Beispiel zu. Dies zeigt, dass eine parallele Ausführung wirklich erforderlich ist, wie auch von Michael bemerkt. Andernfalls werden wir nicht gekündigt.
Alex Vong

2

Der Begriff, der normalerweise in der Dokumentation verwendet wird, ist "Pipeline", die aus einem oder mehreren Befehlen besteht. siehe POSIX-Definition Technisch gesehen sind das also zwei Befehle, zwei Unterprozesse für die Shell (entweder fork()+exec()'ed external commands' oder 'subshells').

Wie für Produzent-Verbraucher Teil betrifft, kann die Pipeline nach diesem Muster beschrieben werden, da:

  • Produzent und Konsument teilen sich einen Puffer mit fester Größe, und zumindest unter Linux und MacOS X gibt es diesen Puffer feste Größe für den Pipeline-Puffer
  • Produzent und Konsument sind lose miteinander verbunden, Befehle in der Pipeline wissen nichts voneinander (es sei denn, sie überprüfen aktiv das /proc/<pid>/fdVerzeichnis).
  • Produzenten schreiben an stdoutund Konsumenten lesen, stdinals ob sie ein einzelnes Kommando ausführen würden, auch wenn sie ohne einander existieren können .

Der Unterschied, den ich hier sehe, besteht darin, dass Shell-Befehle im Gegensatz zu Producer-Consumer in anderen Sprachen die Pufferung verwenden und stdout schreiben, sobald der Puffer gefüllt ist. Es wird jedoch nicht erwähnt, dass Producer-Consumer diese Regel befolgen muss - warten Sie nur, wenn die Warteschlange gefüllt oder verworfen ist Daten (was etwas anderes ist, was die Pipeline nicht tut).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.