Brian Kernighan erklärt in diesem Video die frühe Anziehungskraft von Bell Labs auf kleine Sprachen / Programme, die auf Speicherbeschränkungen basieren
Eine große Maschine würde 64 kByte groß sein - 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 den Pipe-Mechanismus. Grundsätzlich Eingabe Ausgabe Umleitung, ermöglicht es, ein Programm zu einem anderen zu verknüpfen.
Ich verstehe jedoch 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 können.
Aus Wikipedia :
In den meisten Unix-ähnlichen Systemen werden alle Prozesse einer Pipeline gleichzeitig gestartet.Die Streams sind ordnungsgemäß verbunden und werden vom Scheduler zusammen mit allen anderen auf dem Computer ausgeführten Prozessen verwaltet. Ein wichtiger Aspekt dabei, der Unix-Pipes von anderen Pipe-Implementierungen unterscheidet, ist das Konzept der Pufferung: Beispielsweise kann ein Sendeprogramm 5000 Bytes pro Sekunde erzeugen, und ein Empfangsprogramm kann möglicherweise nur 100 Bytes pro Sekunde akzeptieren, aber keine Daten gehen verloren. Stattdessen wird die Ausgabe des Sendeprogramms im Puffer gehalten. Wenn das empfangende Programm zum Lesen von Daten bereit ist, liest das nächste Programm in der Pipeline aus dem Puffer. Unter Linux beträgt die Größe des Puffers 65536 Byte (64 KB). Ein Open-Source-Filter von Drittanbietern namens bfr ist verfügbar, um bei Bedarf größere Puffer bereitzustellen.
Das verwirrt mich umso mehr, als dies den Zweck kleiner Programme völlig zunichte macht (obwohl sie bis zu einem gewissen Ausmaß modular sein würden).
Das einzige, was ich mir als Lösung für meine erste Frage vorstellen kann (die Speicherbeschränkungen sind problematisch und hängen von den Größenangaben ab), ist, dass große Datenmengen damals einfach nicht berechnet wurden und das eigentliche Problem, das Pipelines lösen sollten, das war Speicherplatz, der von den Programmen selbst benötigt wird. Aber angesichts des fettgedruckten Textes im Wikipedia-Zitat verwirrt mich auch das: Es wird immer nur ein Programm gleichzeitig implementiert.
All dies wäre sehr sinnvoll, wenn temporäre Dateien verwendet würden, aber ich verstehe, dass Pipes nicht auf die Festplatte schreiben (es sei denn, Swap wird verwendet).
Beispiel:
sed 'simplesubstitution' file | sort | uniq > file2
Mir sed
ist klar, dass ich die Datei lese und zeilenweise ausspucke. Aber sort
, wie BK in dem verlinkten Video feststellt, ist es ein Punkt, also müssen alle Daten in den Speicher eingelesen werden (oder?), Dann wird es an weitergeleitet uniq
, was (meiner Meinung nach) eine Eins wäre -Zeilen-zu-Zeit-Programm. Aber zwischen der ersten und der zweiten Pipe müssen sich alle Daten im Speicher befinden, oder?
unless swap is used
Swap wird immer dann verwendet , wenn nicht genügend RAMAntworten:
Die Daten müssen nicht im RAM gespeichert werden. Pipes blockieren ihre Autoren, wenn die Leser nicht da sind oder nicht mithalten können. Unter Linux (und den meisten anderen Implementierungen, stelle ich mir vor) gibt es einige Puffer, aber das ist nicht erforderlich. Wie von mtraceur und JdeBP erwähnt (siehe dessen Antwort)), frühe Versionen von Unix, pufferten Pipes auf die Festplatte, und auf diese Weise konnten sie die Speichernutzung begrenzen: Eine Verarbeitungspipeline könnte in kleine Programme aufgeteilt werden, von denen jedes einige Daten innerhalb der Grenzen der Festplattenpuffer verarbeiten würde. Kleine Programme benötigen weniger Speicher und die Verwendung von Pipes bedeutete, dass die Verarbeitung serialisiert werden konnte: Das erste Programm wurde ausgeführt, der Ausgabepuffer wurde gefüllt, das zweite Programm wurde geplant, der Puffer wurde verarbeitet usw. Moderne Systeme sind Aufträge Größer als die frühen Unix-Systeme und kann viele Pipes parallel betreiben. Bei großen Datenmengen wird jedoch immer noch ein ähnlicher Effekt angezeigt (und Varianten dieser Technik werden für die Verarbeitung von „Big Data“ verwendet).
In Ihrem Beispiel
sed
Liest Datenfile
nach Bedarf aus und schreibt sie dann, solange siesort
zum Lesen bereit sind. Wennsort
es nicht fertig ist, blockiert das Schreiben. Die Daten werden zwar irgendwann im Arbeitsspeicher abgelegt, dies ist jedoch spezifischsort
undsort
bereit, Probleme zu lösen (es werden temporäre Dateien verwendet , wenn die zu sortierende Datenmenge zu groß ist).Sie können das Blockierungsverhalten anzeigen, indem Sie ausführen
Dies erzeugt eine ganze Menge von Daten und leitet sie an einen Prozess weiter, der in den ersten zwei Minuten nicht bereit ist, etwas zu lesen . Es wird eine Reihe von
write
Vorgängen angezeigt, die jedoch sehr schnellseq
angehalten werden und auf den Ablauf von zwei Minuten warten, die vom Kernel blockiert werden (derwrite
Systemaufruf wartet).quelle
Dies ist Ihr grundlegender Fehler. Frühe Versionen von Unix enthielten keine Pipe-Daten im RAM. Sie haben sie auf CD gespeichert. Rohre hatten i-Knoten; auf einem Plattengerät, das als Pipe-Gerät bezeichnet wurde . Der Systemadministrator führte ein Programm mit dem Namen aus
/etc/config
, um (unter anderem) anzugeben, auf welchem Volume sich das Pipe-Gerät befand, auf welchem Volume sich das Root-Gerät befand und auf welchem das Dump-Gerät .Die Menge an anstehenden Daten wurde durch die Tatsache eingeschränkt, dass nur die direkten Blöcke des i-Knotens auf der Platte zum Speichern verwendet wurden. Dieser Mechanismus machte den Code einfacher, da für das Lesen aus einer Pipe fast derselbe Algorithmus verwendet wurde wie für das Lesen für eine reguläre Datei, mit einigen Verbesserungen, die durch die Tatsache verursacht wurden, dass Pipes nicht suchbar sind und der Puffer kreisförmig ist.
Dieser Mechanismus wurde Mitte bis Ende der 1980er Jahre durch andere ersetzt. SCO XENIX erhielt das "High Performance Pipe System", das i-Nodes durch In-Core-Puffer ersetzte. 4BSD hat unbenannte Pfeifen zu Muffenpaaren verarbeitet. AT & T hat Pipes mithilfe des STREAMS-Mechanismus neu implementiert.
Und natürlich führte das
sort
Programm eine begrenzte interne Sortierung von 32 KB großen Eingabestücken durch (oder eine beliebige kleinere Menge an Speicher, die zugewiesen werden könnte, wenn 32 KB nicht verfügbar wären) und schrieb die sortierten Ergebnisse in ZwischendateienstmX??
, in/usr/tmp/
denen sie dann extern sortiert zusammengeführt wurden, um das endgültige Ergebnis zu erhalten Ausgabe.Weitere Lektüre
config
(1M)". Unix-Programmierhandbuch: 3. Systemverwaltungsfunktionen . Holt, Rinehart und Winston. ISBN 0030093139. S. 23–28.quelle
Sie haben teilweise recht, aber nur aus Versehen .
In Ihrem Beispiel müssen alle Daten tatsächlich "zwischen" den Pipes gelesen worden sein, sie müssen jedoch nicht im Arbeitsspeicher (einschließlich des virtuellen Arbeitsspeichers) gespeichert sein. Übliche Implementierungen von
sort
können Datensätze sortieren, die nicht in den Arbeitsspeicher passen, indem Teil-Sortierungen in Tempfiles durchgeführt und zusammengeführt werden. Es ist jedoch eine Tatsache, dass Sie unmöglich eine sortierte Sequenz ausgeben können, bevor Sie jedes einzelne Element gelesen haben. Das ist ziemlich offensichtlich. Also ja,sort
ich kann erst mit der Ausgabe an die zweite Pipe beginnen, nachdem ich alles von der ersten Pipe gelesen habe (und was auch immer getan habe, möglicherweise tempfiles teilweise sortiert habe). Es muss aber nicht unbedingt alles im RAM bleiben.Dies hat jedoch nichts mit der Funktionsweise von Rohren zu tun. Pipes können benannt werden (traditionell wurden sie alle benannt), was nicht mehr und nicht weniger bedeutet, als dass sie wie Dateien einen Speicherort im Dateisystem haben. Und genau das waren früher Pipes, Dateien (wobei Schreibvorgänge so weit zusammengeführt wurden, wie es die Verfügbarkeit des physischen Speichers zur Optimierung zulässt).
Heutzutage sind Pipes ein kleiner, endlicher Kernel-Puffer, in den Daten kopiert werden, zumindest geschieht dies konzeptionell . Wenn der Kernel dies unterstützt, werden Kopien durch das Abspielen von VM-Tricks entfernt (z. B. stellt das Weiterleiten aus einer Datei in der Regel nur die gleiche Seite zum Lesen für den anderen Prozess zur Verfügung, sodass es sich letztendlich nur um einen Lesevorgang handelt, nicht um zwei Kopien und nicht um einen Es wird ohnehin mehr Speicher benötigt, als bereits vom Puffercache verwendet wird. In einigen Situationen erhalten Sie möglicherweise auch eine 100% -Null-Kopie. Oder es ist etwas sehr Nahes.
Wenn Pipes klein und endlich groß sind, wie kann dies dann für eine unbekannte (möglicherweise große) Datenmenge funktionieren? Das ist ganz einfach: Wenn nichts mehr passt, blockiert das Schreiben, bis wieder Platz ist.
Die Philosophie vieler einfacher Programme war einmal nützlich, als der Speicher sehr knapp war. Sie können nämlich in kleinen Schritten nacheinander arbeiten. Heutzutage sind die Vorteile, abgesehen von einer gewissen zusätzlichen Flexibilität, meiner Meinung nach nicht mehr so großartig.
Pipes sind jedoch sehr effizient implementiert (sie mussten es sein!), Daher gibt es auch keinen Nachteil, und es ist eine etablierte Sache, die gut funktioniert und an die sich die Leute gewöhnt haben. Es besteht also keine Notwendigkeit, das Paradigma zu ändern.
quelle
|
in einem Befehl verwenden)?