Warum sollten Sie eine Pipe unter Linux schließen?

Lesezeit: 6 Minuten

Benutzeravatar von pghazanfari
pghazanfari

Welchen Zweck hat es, bei der Verwendung einer Pipe für die Prozess-Prozess-Kommunikation ein Ende der Pipe zu schließen?

Zum Beispiel: Wie sendet man eine einfache Zeichenfolge zwischen zwei Programmen mit Pipes?

Beachten Sie, dass eine Seite der Pipe in den untergeordneten und übergeordneten Prozessen geschlossen ist. Warum ist dies erforderlich?

Benutzeravatar von glglgl
glglgl

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

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

Die lesende Seite soll erfahren, dass der Schreiber fertig ist, wenn sie eine EOF-Bedingung bemerkt. Dies kann nur passieren, wenn alle Schreibseiten geschlossen sind. Es ist also am besten, wenn es sein Schreiben 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 ein eventuell vorhandenes Limit an offenen FDs zu erreichen. Außerdem wird der Schreiber darüber benachrichtigt, wenn der dann einzige Leser stirbt, indem er einen SIGPIPE- oder zumindest einen EPIPE-Fehler erhält (je nachdem, wie Signale definiert sind). Sind mehrere Lesegeräte vorhanden, kann der Schreiber nicht erkennen, dass „der Echte“ weggegangen ist, schreibt weiter und bleibt hängen, da das schreibende FD blockiert in der Hoffnung, dass der „unbenutzte“ Leser etwas lesen wird.

Also hier im Detail was passiert:

  • übergeordnete Prozessaufrufe pipe() und erhält 2 Dateideskriptoren: nennen wir es rd und wr.
  • übergeordnete Prozessaufrufe fork(). Jetzt haben beide Prozesse a rd und ein wr.
  • Angenommen, der untergeordnete Prozess soll der Reader sein.

    Dann

    • der Elternteil sollte sein Leseende schließen (um keine FDs zu verschwenden und um den sterbenden Leser richtig zu erkennen) und
    • das Kind muss sein Schreibende schließen (um den EOF-Zustand erkennen zu können).

  • “jedes vorhandene Lesegerät soll seine Daten lesen” – ICH denken Das ist nicht richtig. Wenn eine Pipe zwei Reader p1 und p2 hat und p1 einige Bytes aus der Pipe liest, dann erhält ein read() in p2 die nächsten Bytes. – Alle anderen Argumente sind meiner Meinung nach richtig, insbesondere die EOF-Erkennung.

    –Martin R

    9. Oktober 2013 um 8:02 Uhr


  • @ MartinR mmm … Ich denke, Sie haben Recht. Ich werde meine Antwort ändern.

    – glglgl

    9. Oktober 2013 um 8:33 Uhr

  • Ein ähnliches Argument wie bei der EOF-Erkennung lässt sich auch auf die Schreibrichtung anwenden: Wenn das Leseende geöffnet ist nur im untergeordneten Prozess und der untergeordnete Prozess stirbt, dann gibt das Schreiben in die Pipe EPIPE (oder gibt einen Fehler zurück). Dies würde nicht funktionieren, wenn im übergeordneten Prozess zusätzlich das lesende Ende geöffnet ist. Es ist also wirklich ein symmetrisches Argument.

    –Martin R

    9. Oktober 2013 um 8:45 Uhr

  • @MartinR Du hast wieder Recht. Wenn der „echte Leser“ stirbt und der „unbenutzte“ Leser übrig bleibt, merkt der Schreiber das nicht und schreibt weiter. Das Schreiben von FD blockiert schließlich und es gibt nichts, was wir wieder dagegen tun können. Also nochmal editieren.

    – glglgl

    9. Oktober 2013 um 9:40 Uhr

  • @yangmillstheory Ja, in diesem Fall brauchst du es wahrscheinlich nicht.

    – glglgl

    17. November 2017 um 8:00 Uhr

Jonis Benutzeravatar
Joni

Die Anzahl der Dateideskriptoren, die gleichzeitig geöffnet sein können, ist begrenzt. Wenn Sie weiterhin Pipes öffnen und nicht bald wieder schließen, gehen Ihnen die FDs aus und Sie können nichts mehr öffnen: keine Pipes, keine Dateien, keine Sockets, …

Ein weiterer Grund, warum es wichtig sein kann, das Rohr zu verschließen, ist, wenn das Verschließen selbst eine Bedeutung für die Anwendung hat. Eine übliche Verwendung von Pipes ist beispielsweise das Senden von errno von einem untergeordneten Prozess zum übergeordneten Prozess, wenn verwendet wird fork und exec zu Starten Sie ein externes Programm:

  1. Der Elternteil erstellt die Pipe, ruft an fork einen untergeordneten Prozess zu erstellen, schließt sein schreibendes Ende und versucht, aus der Pipe zu lesen.
  2. Der untergeordnete Prozess versucht zu verwenden exec um ein anderes Programm auszuführen:
    1. Wenn exec scheitert, zum Beispiel weil das Programm nicht existiert, schreibt das Kind errno an die Pipe, und die Eltern lesen es und wissen, was schief gelaufen ist, und können es dem Benutzer mitteilen.
    2. Wenn exec erfolgreich ist, wird die Pipe geschlossen, ohne dass etwas geschrieben wird. Das read Die Funktion in der übergeordneten Funktion gibt 0 zurück, was anzeigt, dass die Pipe geschlossen wurde, und weiß, dass das Programm erfolgreich gestartet wurde.

Wenn der Elternteil sein schreibendes Ende der Pipe nicht schließt, bevor er versucht, aus der Pipe zu lesen, würde dies nicht funktionieren, weil die read Die Funktion würde niemals zurückkehren, wenn exec ist erfolgreich.

Benutzeravatar von Holeryn
Holeryn

Das Schließen unbenutzter Pipe-Dateideskriptoren ist mehr als nur eine Frage der Sicherstellung, dass ein Prozess seinen begrenzten Satz an Dateideskriptoren nicht erschöpft – es ist wesentlich für die korrekte Verwendung von Pipes. Wir betrachten nun, warum die unbenutzten Dateideskriptoren sowohl für das Lese- als auch für das Schreibende der Pipe geschlossen werden müssen. Der Prozess, der aus der Pipe liest, schließt seinen Schreibdeskriptor für die Pipe, so dass, wenn der andere Prozess seine Ausgabe abschließt und seinen Schreibdeskriptor schließt, der Lesevorgang das Dateiende sieht (sobald er alle ausstehenden Daten in der Pipe bereit hat). . Wenn der Leseprozess das Schreibende der Pipe nicht schließt, sieht der Reader, nachdem der andere Prozess seinen Schreibdeskriptor geschlossen hat, kein Dateiende, selbst nachdem er alle Daten aus der Pipe gelesen hat. Stattdessen ein read() würde das Warten auf Daten blockieren, da der Kernel weiß, dass noch mindestens ein Write-Deskriptor für die Pipe offen ist. Dass dieser Deskriptor vom Leseprozess selbst offen gehalten wird, ist irrelevant; Theoretisch könnte dieser Prozess immer noch in die Pipe schreiben, selbst wenn er beim Lesen blockiert wird. Zum Beispiel die read() könnte durch einen Signalhandler unterbrochen werden, der Daten in die Pipe schreibt. Der Schreibprozess schließt seinen Lesedeskriptor für die Pipe aus einem anderen Grund. Wenn ein Prozess versucht, in eine Pipe zu schreiben, für die kein Prozess einen offenen Lesedeskriptor hat, sendet der Kernel die SIGPIPE Signal für den Schreibprozess. Standardmäßig beendet dieses Signal einen Prozess. Ein Prozess kann stattdessen veranlassen, dieses Signal abzufangen oder zu ignorieren, in welchem ​​Fall das write() auf der Pipe schlägt mit dem Fehler fehl EPIPE (Rohrbruch). Erhalt der SIGPIPE Signal oder immer die EPIPE error ist ein nützlicher Hinweis auf den Status der Pipe, und deshalb sollten unbenutzte Lesedeskriptoren für die Pipe geschlossen werden. Wenn der Schreibprozess das Leseende der Pipe nicht schließt, wird der Schreibprozess die Pipe füllen, selbst nachdem der andere Prozess das Leseende der Pipe geschlossen hat, und ein weiterer Schreibversuch wird auf unbestimmte Zeit blockiert. Ein letzter Grund für das Schließen unbenutzter Dateideskriptoren ist, dass erst nachdem alle Dateideskriptoren geschlossen wurden, die Pipe zerstört und ihre Ressourcen zur Wiederverwendung durch andere Prozesse freigegeben werden. An diesem Punkt gehen alle ungelesenen Daten in der Pipe verloren.

~ Michael Kerrisk, die Linux-Programmierschnittstelle

  • Dies wäre eine großartige Antwort, wenn sie in Absätze gespuckt würde

    – Daniel Pop

    26. Juni 2021 um 9:20 Uhr

1402350cookie-checkWarum sollten Sie eine Pipe unter Linux schließen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy