GNU/Linux >> LINUX-Kenntnisse >  >> Linux

O_RDWR auf Named Pipes mit poll()

Laut manpage open(2) können Sie O_RDONLY|O_NONBLOCK übergeben oder O_WRONLY|O_NONBLOCK um die open zu vermeiden Systemaufruf blockiert werden (Sie erhalten errno == ENXIO in diesem Fall)

Wie ich kommentiert habe, lesen Sie auch die Manpages fifo(7) und mkfifo(3).


Zuerst einige Vorbereitungen:

Mit O_NONBLOCK und poll() ist gängige Praxis – nicht umgekehrt. Um erfolgreich zu arbeiten, müssen Sie sicherstellen, dass alle poll() verarbeitet werden und read() Rückgabezustände korrekt:

  • read() Rückgabewert von 0 bedeutet EOF - die andere Seite hat ihre Verbindung geschlossen. Dies entspricht (normalerweise, aber nicht auf allen Betriebssystemen) poll() Rückgabe eines POLLHUP revent. Vielleicht möchten Sie nach POLLHUP suchen bevor Sie read() versuchen , ist aber seit read() nicht zwingend erforderlich gibt garantiert 0 zurück nach dem Schließen der Schreibseite.
  • Wenn Sie read() anrufen bevor sich ein Autor verbunden hat, und Sie haben O_RDONLY | O_NONBLOCK , erhalten Sie EOF (read() Zurückgeben von 0 ) wiederholt, wie Sie bemerkt haben. Wenn Sie jedoch poll() verwenden um auf POLLIN zu warten Ereignis vor dem Aufruf von read() , wartet er darauf, dass der Writer eine Verbindung herstellt, und erzeugt keine EOFs.
  • read() Rückgabewert -1 bedeutet normalerweise Fehler. Wenn jedoch errno == EAGAIN , bedeutet dies einfach, dass im Moment keine Daten mehr verfügbar sind und Sie nicht blockieren, sodass Sie zu poll() zurückkehren können falls andere Geräte behandelt werden müssen. Wenn errno == EINTR , dann read() wurde vor dem Lesen von Daten unterbrochen, und Sie können entweder zu poll() zurückkehren oder rufen Sie einfach read() an sofort wieder.

Jetzt für Linux:

  • Wenn Sie auf der Leseseite mit O_RDONLY öffnen , dann:
    • Der open() blockiert, bis ein entsprechender Writer geöffnet ist.
    • poll() ergibt einen POLLIN revent, wenn Daten zum Lesen bereit sind oder EOF auftritt.
    • read() blockiert, bis entweder die angeforderte Anzahl von Bytes gelesen wurde, die Verbindung geschlossen wird (0 zurückgibt), sie durch ein Signal unterbrochen wird oder ein schwerwiegender E/A-Fehler auftritt. Diese Blockierung macht den Zweck der Verwendung von poll() zunichte , weshalb poll() wird fast immer mit O_NONBLOCK verwendet . Sie könnten einen alarm() verwenden um aus read() aufzuwachen nach einem Timeout, aber das ist zu kompliziert.
    • Wenn der Schreiber schließt, erhält der Leser einen poll() POLLHUP revent und read() gibt 0 zurück danach auf unbestimmte Zeit. An diesem Punkt muss der Reader sein Dateihandle schließen und erneut öffnen.
  • Wenn Sie auf der Leseseite mit O_RDONLY | O_NONBLOCK öffnen , dann:
    • Der open() wird nicht blockiert.
    • poll() gibt einen POLLIN aus revent, wenn Daten zum Lesen bereit sind oder EOF auftritt. poll() blockiert auch, bis ein Autor verfügbar ist, falls keiner vorhanden ist.
    • Nachdem alle aktuell verfügbaren Daten gelesen wurden, read() gibt entweder -1 zurück und setzt errno == EAGAIN wenn die Verbindung noch offen ist, oder es wird 0 zurückgegeben wenn die Verbindung geschlossen (EOF) oder noch nicht von einem Schreiber geöffnet ist . Wenn errno == EAGAIN , das bedeutet, dass es Zeit ist, zu poll() zurückzukehren , da die Verbindung geöffnet ist, aber keine Daten mehr vorhanden sind. Wenn errno == EINTR , read() hat noch keine Bytes gelesen und wurde durch ein Signal unterbrochen, kann also neu gestartet werden.
    • Wenn der Schreiber schließt, erhält der Leser einen poll() POLLHUP revent und read() gibt 0 zurück danach auf unbestimmte Zeit. An diesem Punkt muss der Reader sein Dateihandle schließen und erneut öffnen.
  • (Linux-spezifisch:) Öffnet man auf der Leseseite mit O_RDWR , dann:
    • Der open() wird nicht blockiert.
    • poll() ergibt einen POLLIN neu starten, wenn die Daten zum Lesen bereit sind. Für benannte Pipes verursacht EOF jedoch nicht POLLIN oder POLLHUP Einnahmen.
    • read() blockiert, bis die angeforderte Anzahl von Bytes gelesen wurde, es durch ein Signal unterbrochen wird oder ein anderer schwerwiegender E/A-Fehler auftritt. Für benannte Pipes wird errno == EAGAIN nicht zurückgegeben , noch wird es 0 zurückgeben einer von. Es bleibt einfach dort, bis die genaue Anzahl der angeforderten Bytes gelesen wurde oder bis es ein Signal empfängt (in diesem Fall gibt es die Anzahl der bisher gelesenen Bytes zurück oder gibt -1 zurück und setzt errno == EINTR wenn noch keine Bytes gelesen wurden).
    • Wenn der Writer schließt, verliert der Reader nicht die Fähigkeit, die Named Pipe später zu lesen, wenn ein anderer Writer die Named Pipe öffnet, aber der Reader erhält auch keine Benachrichtigung.
  • (Linux-spezifisch:) Öffnet man auf der Leseseite mit O_RDWR | O_NONBLOCK , dann:
    • Die open() wird nicht blockiert.
    • poll() gibt einen POLLIN neu starten, wenn die Daten zum Lesen bereit sind. EOF verursacht jedoch nicht POLLIN oder POLLHUP nutzt Named Pipes.
    • Nachdem alle aktuell verfügbaren Daten gelesen wurden, read() gibt -1 zurück und setzen Sie errno == EAGAIN . Dies ist die Zeit, zu poll() zurückzukehren um auf weitere Daten zu warten, möglicherweise von anderen Streams.
    • Wenn der Writer schließt, verliert der Leser nicht die Fähigkeit, die Named Pipe später zu lesen, wenn ein anderer Writer die Named Pipe öffnet. Die Verbindung ist dauerhaft.

Wie Sie zu Recht befürchten, verwenden Sie O_RDWR mit Pipes ist kein Standard, POSIX oder anderswo.

Da diese Frage jedoch häufig aufzutauchen scheint, ist der beste Weg unter Linux, "widerstandsfähige Named Pipes" zu erstellen, die auch dann am Leben bleiben, wenn eine Seite geschlossen wird, und die nicht POLLHUP verursachen revents oder gibt 0 zurück für read() , soll O_RDWR | O_NONBLOCK verwenden .

Ich sehe drei Hauptwege für den Umgang mit Named Pipes unter Linux:

  1. (Tragbar.) Ohne poll() , und mit einer einzelnen Pipe:

    • open(pipe, O_RDONLY);
    • Hauptschleife:
      • read() so viele Daten wie nötig, möglicherweise Schleife auf read() Anrufe.
        • Falls read() == -1 und errno == EINTR , read() immer wieder.
        • Falls read() == 0 , die Verbindung wird geschlossen und alle Daten wurden empfangen.

  2. (Tragbar.) Mit poll() , und mit der Erwartung, dass Pipes, selbst benannte, nur einmal geöffnet werden und dass, sobald sie geschlossen sind, sowohl vom Leser als auch vom Schreiber wieder geöffnet werden müssen, wodurch eine neue Pipeline eingerichtet wird:

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • Hauptschleife:
      • poll() für POLLIN Ereignisse, möglicherweise auf mehreren Pipes gleichzeitig. (Hinweis:Dies verhindert read() davor, mehrere EOFs zu erhalten, bevor ein Schreiber eine Verbindung hergestellt hat.)
      • read() so viele Daten wie nötig, möglicherweise Schleife auf read() Anrufe.
        • Wenn read() == -1 und errno == EAGAIN , gehen Sie zurück zu poll() Schritt.
        • Falls read() == -1 und errno == EINTR , read() immer wieder.
        • Falls read() == 0 , wird die Verbindung geschlossen, und Sie müssen die Pipe beenden oder schließen und erneut öffnen.

  3. (Nicht portierbar, Linux-spezifisch.) Mit poll() , und mit der Erwartung, dass Named Pipes niemals enden und mehrmals verbunden und getrennt werden können:

    • open(pipe, O_RDWR | O_NONBLOCK);
    • Hauptschleife:
      • poll() für POLLIN Ereignisse, möglicherweise auf mehreren Pipes gleichzeitig.
      • read() so viele Daten wie nötig, möglicherweise Schleife auf read() Anrufe.
        • Wenn read() == -1 und errno == EAGAIN , gehen Sie zurück zu poll() Schritt.
        • Wenn read() == -1 und errno == EINTR , read() immer wieder.
        • Wenn read() == 0 , etwas stimmt nicht – es sollte nicht mit O_RDWR passieren auf Named Pipes, aber nur mit O_RDONLY oder unbenannte Rohre; es zeigt ein geschlossenes Rohr an, das geschlossen und wieder geöffnet werden muss. Wenn Sie benannte und unbenannte Pipes im selben poll() mischen Event-Handling-Schleife, dieser Fall muss möglicherweise noch behandelt werden.

Linux
  1. Arbeiten mit Pipes auf der Linux-Befehlszeile

  2. Lesen von Zeilen aus einer Datei mit Bash:Für Vs. Während?

  3. Was macht poll() mit einem Timeout von 0?

  4. Ein fehlerhaftes Blockgerät mit Lesefehlern simulieren?

  5. Linux:Gibt es ein Lesen oder Empfangen vom Socket mit Timeout?

Eine Einführung in Pipes und Named Pipes in Linux

Linux mkfifo Command Tutorial für Anfänger (mit Beispielen)

Verwendung von Pipes und Named Pipes unter Linux (mit Beispielen)

Lesen Sie E-Books von der Befehlszeile mit Epy Ebook Reader

Vim-Tipps – Lesen und schreiben Sie entfernte Dateien mit Vim unter Linux

Bedingte DNS-Weiterleitung mit named unter Linux