Ich habe die andere Frage recherchiert , als mir klar wurde, dass ich nicht verstehe, was unter der Haube passiert, was diese /dev/fd/*
Dateien sind und warum untergeordnete Prozesse sie öffnen können.
bash
process-substitution
x-yuri
quelle
quelle
Antworten:
Nun, es gibt viele Aspekte.
Dateideskriptoren
Für jeden Prozess verwaltet der Kernel eine Tabelle mit geöffneten Dateien (möglicherweise ist diese Tabelle anders implementiert, aber da Sie sie sowieso nicht sehen können, können Sie davon ausgehen, dass es sich um eine einfache Tabelle handelt). Diese Tabelle enthält Informationen darüber, um welche Datei es sich handelt / wo sie sich befindet, in welchem Modus Sie sie geöffnet haben, an welcher Position Sie gerade lesen / schreiben und was sonst noch benötigt wird, um tatsächlich E / A-Operationen an dieser Datei durchzuführen. Jetzt kann der Prozess diese Tabelle nie mehr lesen (oder sogar schreiben). Wenn der Prozess eine Datei öffnet, erhält er einen sogenannten Dateideskriptor zurück. Welches ist einfach ein Index in die Tabelle.
Das Verzeichnis
/dev/fd
und sein InhaltUnter Linux
dev/fd
ist das eigentlich eine symbolische Verknüpfung zu/proc/self/fd
./proc
ist ein Pseudodateisystem, in dem der Kernel mehrere interne Datenstrukturen abbildet, auf die mit der Datei-API zugegriffen werden kann (sie sehen also wie normale Dateien / Verzeichnisse / Symlinks zu den Programmen aus). Insbesondere gibt es Informationen zu allen Prozessen (was ihm den Namen gab). Die symbolische Verknüpfung/proc/self
bezieht sich immer auf das Verzeichnis, das dem aktuell ausgeführten Prozess zugeordnet ist (dh auf den Prozess, der ihn anfordert; verschiedene Prozesse sehen daher unterschiedliche Werte). Im Verzeichnis des Prozesses befindet sich ein Unterverzeichnisfd
Die Datei enthält für jede geöffnete Datei eine symbolische Verknüpfung, deren Name nur die dezimale Darstellung des Dateideskriptors ist (der Index in der Dateitabelle des Prozesses, siehe vorherigen Abschnitt) und deren Ziel die Datei ist, der sie entspricht.Dateideskriptoren beim Erstellen von untergeordneten Prozessen
Ein untergeordneter Prozess wird von a erstellt
fork
. A erstelltfork
eine Kopie der Dateideskriptoren. Dies bedeutet, dass der erstellte untergeordnete Prozess dieselbe Liste offener Dateien enthält wie der übergeordnete Prozess. Wenn eine der geöffneten Dateien nicht vom untergeordneten Element geschlossen wird, greift der Zugriff auf einen geerbten Dateideskriptor im untergeordneten Element auf dieselbe Datei zu wie der Zugriff auf den ursprünglichen Dateideskriptor im übergeordneten Prozess.Beachten Sie, dass Sie nach einem Fork zunächst zwei Kopien desselben Prozesses haben, die sich nur im Rückgabewert vom Fork-Aufruf unterscheiden (das Elternteil erhält die PID des Kindes, das Kind erhält 0). Normalerweise folgt einem Fork ein
exec
, um eine der Kopien durch eine andere ausführbare Datei zu ersetzen. Die offenen Dateideskriptoren überleben diesen exec. Beachten Sie auch, dass der Prozess vor der Ausführung andere Manipulationen ausführen kann (z. B. das Schließen von Dateien, die der neue Prozess nicht erhalten sollte, oder das Öffnen anderer Dateien).Unbenannte Rohre
Eine unbenannte Pipe ist nur ein Paar von Dateideskriptoren, die auf Anforderung des Kernels erstellt wurden, sodass alles, was in den ersten Dateideskriptor geschrieben wurde, an den zweiten übergeben wird. Am häufigsten wird das Piping-Konstrukt
foo | bar
von verwendetbash
, bei dem die Standardausgabe vonfoo
durch den Schreibteil der Pipe und die Standardeingabe durch den Leseteil ersetzt wird. Standardeingabe und Standardausgabe sind nur die ersten beiden Einträge in der Dateitabelle (Eintrag 0 und 1; 2 ist Standardfehler), und daher bedeutet das Ersetzen dieses Eintrags nur das Umschreiben dieses Tabelleneintrags mit den Daten, die dem anderen Dateideskriptor entsprechen (wieder der Die tatsächliche Implementierung kann davon abweichen. Da der Prozess nicht direkt auf die Tabelle zugreifen kann, gibt es dafür eine Kernelfunktion.Prozessersetzung
Jetzt haben wir alles zusammen, um zu verstehen, wie die Prozessersetzung funktioniert:
echo
Prozess. Der untergeordnete Prozess (der eine exakte Kopie des ursprünglichenbash
Prozesses ist) schließt das Leseende der Pipe und ersetzt die eigene Standardausgabe durch das Schreibende der Pipe.echo
Vorausgesetzt, dass es sich um eine eingebaute Shell handelt, erspart sichbash
möglicherweise denexec
Aufruf, spielt jedoch keine Rolle (die eingebaute Shell ist möglicherweise ebenfalls deaktiviert und wird in diesem Fall ausgeführt/bin/echo
).<(echo 1)
durch die Pseudodateiverknüpfung in/dev/fd
Bezug auf das Leseende der unbenannten Pipe./dev/fd/
. Da der entsprechende Dateideskriptor noch offen ist, entspricht er immer noch dem Leseende der Pipe. Wenn das PHP-Programm die angegebene Datei zum Lesen öffnet, erstellt es tatsächlich einensecond
Dateideskriptor für das Leseende der unbenannten Pipe. Aber das ist kein Problem, das könnte man auch lesen.echo
Befehls, der an das Schreibende derselben Pipe geht.quelle
php
Szenario, aber esphp
geht nicht gut mit Rohren um . In Anbetracht des Kommandoscat <(echo test)
ist das Seltsame hier, dass sich diebash
Gabeln einmalcat
, aber zweimal teilenecho test
.Das Ausleihen aus
celtschk
der Antwort/dev/fd
ist eine symbolische Verknüpfung zu/proc/self/fd
. Und/proc
ist ein Pseudo-Dateisystem, das Informationen über Prozesse und andere Systeminformationen in einer hierarchischen dateiähnlichen Struktur darstellt. Dateien in/dev/fd
entsprechen Dateien, die von einem Prozess geöffnet wurden, und haben einen Dateideskriptor als Namen und Dateien selbst als Ziele. Das Öffnen der Datei/dev/fd/N
entspricht dem Duplizieren des DeskriptorsN
(vorausgesetzt, der DeskriptorN
ist geöffnet).Und hier sind die Ergebnisse meiner Untersuchung, wie es funktioniert (die
strace
Ausgabe ist frei von unnötigen Details und wurde modifiziert, um besser auszudrücken, was passiert):Erstellt im
bash
Allgemeinen eine Pipe und übergibt ihre Enden als Dateideskriptoren an ihre untergeordneten Elemente (Leseende an1.out
und Schreibende an2.out
). Und übergibt read end als Befehlszeilenparameter an1.out
(/dev/fd/63
). Dieser Weg1.out
kann sich öffnen/dev/fd/63
.quelle