Brian Kernighan erklärt in diesem Video die frühe Attraktivität der Bell Labs für kleine Sprachen/Programme, die auf Speicherbeschränkungen basieren
Eine große Maschine hätte 64 kByte – K, nicht M oder G – und das bedeutete, dass jedes einzelne Programm nicht sehr groß sein konnte, und so gab es eine natürliche Tendenz, kleine Programme zu schreiben, und dann der Pipe-Mechanismus, im Grunde genommen Eingaben Ausgabeumleitung, ermöglichte es, ein Programm mit einem anderen zu verknüpfen.
Aber ich verstehe nicht, wie dies die Speichernutzung einschränken könnte, wenn man bedenkt, dass die Daten im RAM gespeichert werden müssen, um zwischen Programmen übertragen zu werden.
Aus Wikipedia:
In den meisten Unix-ähnlichen Systemen werden alle Prozesse einer Pipeline gleichzeitig gestartet [Hervorhebung von mir] , deren Streams entsprechend verbunden und vom Scheduler zusammen mit allen anderen Prozessen verwaltet werden, die auf dem Computer ausgeführt werden. Ein wichtiger Aspekt, der Unix-Pipes von anderen Pipe-Implementierungen unterscheidet, ist das Konzept der Pufferung:Beispielsweise kann ein sendendes Programm 5000 Bytes pro Sekunde produzieren, und ein empfangendes Programm kann möglicherweise nur 100 Bytes pro Sekunde akzeptieren, aber nein Daten gehen verloren. Stattdessen wird die Ausgabe des sendenden Programms im Puffer gehalten. Wenn das empfangende Programm bereit ist, Daten zu lesen, liest das nächste Programm in der Pipeline aus dem Puffer. Unter Linux beträgt die Größe des Puffers 65536 Bytes (64 KB). Ein Open-Source-Drittanbieterfilter namens bfr ist verfügbar, um bei Bedarf größere Puffer bereitzustellen.
Dies verwirrt mich noch mehr, da dies den Zweck kleiner Programme völlig zunichte macht (obwohl sie bis zu einem bestimmten Umfang modular sein würden).
Das einzige, was mir als Lösung für meine erste Frage einfällt (die Speicherbeschränkungen sind problematisch, abhängig von der Größe der Daten), wäre, dass große Datensätze damals einfach nicht berechnet wurden und das eigentliche Problem, das Pipelines lösen sollten, das war Menge an Speicher, die von den Programmen selbst benötigt wird. Aber angesichts des fettgedruckten Textes im Wikipedia-Zitat verwirrt mich selbst das:da nicht jeweils ein Programm implementiert wird.
All dies würde sehr viel Sinn machen, wenn temporäre Dateien verwendet würden, aber meines Wissens schreiben Pipes nicht auf die Festplatte (es sei denn, es wird Swap verwendet).
Beispiel:
sed 'simplesubstitution' file | sort | uniq > file2
Mir ist klar, dass sed
liest die Datei ein und gibt sie zeilenweise aus. Aber sort
, wie BK im verlinkten Video feststellt, ist ein Punkt, also müssen alle Daten in den Speicher gelesen werden (oder doch?), dann werden sie an uniq
weitergegeben , was (meiner Meinung nach) ein One-Line-at-a-Time-Programm wäre. Aber zwischen der ersten und zweiten Pipe müssen alle Daten im Speicher sein, oder?
Akzeptierte Antwort:
Die Daten müssen nicht im RAM gespeichert werden. Pipes blockieren ihre Schreiber, wenn die Leser nicht da sind oder nicht mithalten können; Unter Linux (und den meisten anderen Implementierungen, stelle ich mir vor) gibt es etwas Pufferung, aber das ist nicht erforderlich. Wie von mtraceur und JdeBP erwähnt (siehe die Antwort des letzteren), haben frühe Versionen von Unix Pipes auf die Festplatte gepuffert, und so haben sie dazu beigetragen, die Speichernutzung zu begrenzen:Eine Verarbeitungspipeline konnte in kleine Programme aufgeteilt werden, von denen jedes einige Daten verarbeiten würde , innerhalb der Grenzen der Plattenpuffer. Kleine Programme benötigen weniger Speicher, und die Verwendung von Pipes bedeutete, dass die Verarbeitung serialisiert werden konnte:Das erste Programm würde ausgeführt, seinen Ausgabepuffer füllen, angehalten, dann das zweite Programm geplant, den Puffer verarbeiten usw. Moderne Systeme sind Befehle um Größenordnungen größer als die frühen Unix-Systeme und kann viele Pipes parallel ausführen; aber bei großen Datenmengen sieht man immer noch einen ähnlichen Effekt (und Varianten dieser Art von Technik werden für die Verarbeitung von „Big Data“ verwendet).
In Ihrem Beispiel
sed 'simplesubstitution' file | sort | uniq > file2
sed
liest Daten aus file
ggf. schreibt es dann so lange wie sort
ist bereit, es zu lesen; wenn sort
nicht bereit ist, blockiert das Schreiben. Die Daten leben tatsächlich irgendwann im Speicher, aber das ist spezifisch für sort
, und sort
ist bereit, sich mit allen Problemen zu befassen (es werden temporäre Dateien verwendet, wenn die zu sortierende Datenmenge zu groß ist).
Sie können das Blockierungsverhalten sehen, indem Sie
ausführenstrace seq 1000000 -1 1 | (sleep 120; sort -n)
Dies erzeugt eine beträchtliche Menge an Daten und leitet sie an einen Prozess weiter, der nicht bereit ist, irgendetwas zu lesen für die ersten zwei Minuten. Sie sehen eine Reihe von write
Operationen durchlaufen, aber sehr schnell seq
wird anhalten und warten, bis die zwei Minuten abgelaufen sind, die vom Kernel blockiert werden (das write
Systemaufruf wartet).