44 Stimmen

Warum sollte man in Linux ein Rohr schließen?

Bei der Verwendung eines Rohrs für die Kommunikation zwischen Prozessen, was ist der Zweck, ein Ende des Rohrs zu schließen?

Zum Beispiel: Wie sendet man einen einfachen String zwischen zwei Programmen mit Rohren?

Beachten Sie, dass auf einer Seite des Rohrs in den Kind- und Elternprozessen geschlossen wird. Warum ist das erforderlich?

71voto

glglgl Punkte 84928

Wenn Sie zwei Prozesse - Eltern und Kind - mit einer Pipe verbinden, erstellen Sie die Pipe vor dem Fork.

Der Fork sorgt dafür, dass beide Prozesse Zugriff auf beide Enden der Pipe haben. Dies ist nicht wünschenswert.

Die Leseseite soll lernen, dass der Schreiber fertig ist, wenn sie einen EOF-Zustand feststellt. Dies kann nur passieren, wenn alle Schreibseiten geschlossen sind. Daher ist es am besten, wenn es seine Schreib-FD so schnell wie möglich schließt.

Der Schreiber sollte seine Lese-FD schließen, nur um nicht zu viele FDs offen zu haben und damit möglicherweise eine Grenze der offenen FDs zu erreichen. Außerdem wird der Schreiber benachrichtigt, wenn der einzige Leser stirbt, indem er ein SIGPIPE oder zumindest einen EPIPE-Fehler erhält (abhängig davon, wie Signale definiert sind). Wenn es mehrere Leser gibt, kann der Schreiber nicht erkennen, dass "der echte" Leser weggegangen ist, schreibt weiter und bleibt stecken, da die Schreib-FD blockiert ist in der Hoffnung, dass der "ungeniutzte" Leser etwas lesen wird.

Hier wird im Detail beschrieben, was passiert:

  • Der Elternprozess ruft pipe() auf und erhält 2 Dateideskriptoren: nennen wir sie rd und wr.
  • Der Elternprozess ruft fork() auf. Jetzt haben beide Prozesse ein rd und ein wr.
  • Angenommen, der Kindprozess soll der Leser sein.

    Dann

    • Der Elternprozess sollte sein Lesende schließen (um keine FDs zu verschwenden und um das Sterben des Lesers korrekt zu erkennen) und
    • Das Kind muss sein Schreibende schließen (um den EOF-Zustand erkennen zu können).

10voto

Joni Punkte 104918

Die Anzahl der Dateideskriptoren, die gleichzeitig geöffnet werden können, ist begrenzt. Wenn Sie weiterhin Pipes öffnen und sie nicht schließen, werden Ihnen bald die FDs ausgehen und Sie können nichts mehr öffnen: weder Pipes, noch Dateien, noch Sockets, ...

Ein weiterer Grund, warum es wichtig sein kann, die Pipe zu schließen, ist, wenn das Schließen selbst eine Bedeutung für die Anwendung hat. Zum Beispiel wird eine häufige Verwendung von Pipes darin gesehen, das errno von einem Kindprozess an das Elternteil zu senden, wenn fork und exec verwendet werden, um ein externes Programm zu starten:

  1. Das Elternteil erstellt die Pipe, ruft fork auf, um einen Kindprozess zu erstellen, schließt sein Schreibende und versucht, von der Pipe zu lesen.
  2. Der Kindprozess versucht, exec auszuführen, um ein anderes Programm zu starten:
    1. Wenn exec fehlschlägt, beispielsweise weil das Programm nicht existiert, schreibt das Kind errno in die Pipe, und das Elternteil liest es und weiß, was schief gelaufen ist, und kann es dem Benutzer mitteilen.
    2. Wenn exec erfolgreich ist, wird die Pipe ohne das Schreiben von etwas geschlossen. Die read-Funktion im Elternteil gibt 0 zurück, was anzeigt, dass die Pipe geschlossen wurde und weiß, dass das Programm erfolgreich gestartet wurde.

Wenn das Elternteil sein Schreibende der Pipe nicht geschlossen hat, bevor es versucht hat, von der Pipe zu lesen, würde dies nicht funktionieren, weil die read-Funktion nie zurückgeben würde, wenn exec erfolgreich ist.

8voto

Holeryn Punkte 377

Das Schließen von ungenutzten Pipe-Dateideskriptoren ist mehr als eine Maßnahme, um sicherzustellen, dass ein Prozess seinen begrenzten Satz von Dateideskriptoren nicht erschöpft - es ist unerlässlich für die korrekte Verwendung von Pipes. Wir betrachten nun, warum die ungenutzten Dateideskriptoren für beide Lese- und Schreibenden der Pipe geschlossen werden müssen. Der Prozess, der von der Pipe liest, schließt seinen Schreib-Deskriptor für die Pipe, so dass, wenn der andere Prozess seine Ausgabe beendet und seinen Schreib-Deskriptor schließt, das Lesen das Dateiende sieht (nachdem es alle ausstehenden Daten in der Pipe gelesen hat). Wenn der lesende Prozess das Schreibende der Pipe nicht schließt, dann wird der Leser nach dem Schließen des Schreib-Deskriptors des anderen Prozesses kein Dateiende sehen, auch nachdem er alle Daten aus der Pipe gelesen hat. Stattdessen würde ein read() blockieren und auf Daten warten, weil der Kernel weiß, dass noch mindestens ein Schreib-Deskriptor für die Pipe geöffnet ist. Dass dieser Deskriptor vom lesenden Prozess selbst offen gehalten wird, ist irrelevant; Theoretisch könnte dieser Prozess immer noch in die Pipe schreiben, auch wenn er beim Lesen blockiert ist. Zum Beispiel könnte das read() durch einen Signalhandler unterbrochen werden, der Daten in die Pipe schreibt. Der schreibende Prozess schließt aus einem anderen Grund seinen Lese-Deskriptor für die Pipe. Wenn ein Prozess versucht, in eine Pipe zu schreiben, für die kein Prozess einen geöffneten Lese-Deskriptor hat, sendet der Kernel das SIGPIPE-Signal an den schreibenden Prozess. Standardmäßig beendet dieses Signal einen Prozess. Ein Prozess kann stattdessen arrangieren, dieses Signal zu fangen oder zu ignorieren, in welchem Fall das write() an der Pipe mit dem Fehler EPIPE (broken pipe) fehlschlägt. Das Erhalten des SIGPIPE-Signals oder den EPIPE-Fehler ist eine nützliche Information über den Status der Pipe, und deshalb sollten ungenutzte Lese-Deskriptoren für die Pipe geschlossen werden. Wenn der schreibende Prozess das Lesende der Pipe nicht schließt, dann wird der schreibende Prozess selbst nachdem der andere Prozess das Lesende der Pipe geschlossen hat die Pipe füllen, und ein weiterer Versuch zu schreiben wird undefiniert blockieren. Ein weiterer Grund für das Schließen ungenutzter Dateideskriptoren ist, dass erst nachdem alle Deskriptoren geschlossen sind, die Pipe zerstört wird und ihre Ressourcen für die Wiederverwendung durch andere Prozesse freigegeben werden. Zu diesem Zeitpunkt gehen alle ungelesenen Daten in der Pipe verloren.

~ Micheal Kerrisk, das Linux-Programmierschnittstellenbuch

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X