Eine benannte Pfeife lesen: Schwanz oder Katze?

7

Ich habe einen Dateideskriptor mit erstellt

mkfifo fifo

Sobald etwas in diese Pipe geschrieben ist, möchte ich es sofort wiederverwenden . Sollte ich es benutzen

tail -f fifo

oder

while true; do cat fifo; done

?

Sie scheinen dasselbe zu tun und ich konnte keinen Leistungsunterschied messen. Wenn ein System jedoch nicht inotify unterstützt (z. B. Busybox), muss dies der Fall sein

tail -f -s 0 fifo

Dies verbraucht jedoch die CPU zu 100% (testen Sie es aus: mkfifo fifo && busybox tail -f -s 0 fifo & echo hi>fifo/ Abbrechen mit fg 1und CtrlC). Ist die while-true-cat die zuverlässigere Lösung?

phil294
quelle
Rohre sind nicht suchbar und tail -fbenötigen suchbare Eingaben. Das Verhalten von tail -f fifoist undefiniert. Wenn tail -f -s 0 fifoauf einigen Systemen funktioniert, verlassen sich diese Systeme auf nicht standardmäßiges Verhalten.
Satō Katsura
@ SatōKatsura, das stimmt nicht. tailfunktioniert gut auf fifos und ist erforderlich. -sist jedoch nicht Standard.
Stéphane Chazelas
@ StéphaneChazelas tailallein funktioniert gut, tail -fbenötigt aber suchbare Eingabe.
Satō Katsura
@ SatōKatsura nein. Das tut es nicht. Es gibt keinen Grund, warum es suchen möchte. Nachdem es die letzten 10 Zeilen ausgegeben hat, sitzt es einfach da und wartet auf weitere Eingabe.
Stéphane Chazelas
@ StéphaneChazelas Es werden suchbare Eingaben benötigt, um Eingabefehler zu beheben. Unter anderem tail -f fifosoll noch nachgelesen werden echo foo >fifo.
Satō Katsura

Antworten:

8

Wenn Sie das tun:

cat fifo

Angenommen, noch kein anderer Prozess hat das fifozum Schreiben geöffnet , catwird der open()Systemaufruf blockiert . Wenn ein anderer Prozess die Datei zum Schreiben öffnet, wird eine Pipe instanziiert und open()zurückgegeben. catruft read()eine Schleife auf und read()blockiert, bis ein anderer Prozess Daten in die Pipe schreibt.

catwird das Dateiende (eof) sehen, wenn alle anderen Schreibvorgänge ihren Dateideskriptor für das geschlossen haben fifo. An welchen Stellen catendet und das Rohr zerstört wird¹.

Sie müssten caterneut ausführen , um zu lesen, was danach in die fifo(aber über eine andere Pipe-Instanz) geschrieben wird.

Im:

tail -f file

Warten Sie cat, tailbis ein Prozess eine Datei zum Schreiben geöffnet hat. Da Sie jedoch -n +1von Anfang an kein zu kopierendes tailElement angegeben haben, müssen Sie bis eof warten, um herauszufinden, was die letzten 10 Zeilen waren, sodass Sie nichts sehen, bis das Schreibende geschlossen ist.

Danach tailwird das fd nicht für die Pipe geschlossen, was bedeutet, dass die Pipe-Instanz nicht zerstört wird, und es wird weiterhin versucht, jede Sekunde aus der Pipe zu lesen (unter Linux kann das Abrufen durch die Verwendung von inotifyund einiger Versionen von vermieden werden GNU tailmacht das dort). Das read()wird wieder mit eof (sofort, weshalb Sie zu 100% CPU sehen mit -s 0(die mit GNU tailMitteln nicht zwischen warten read()s statt für eine Sekunde des Wartens)) , bis ein anderen Prozess die Datei erneut zum Schreiben öffnet.

Hier möchten Sie möglicherweise stattdessen verwenden cat, aber stellen Sie sicher, dass die Pipe-Instanz nach der Instanziierung immer verfügbar bleibt. Dafür können Sie auf den meisten Systemen Folgendes tun:

cat 0<> fifo # the 0 is needed for recent versions of ksh93 where the
             # default fd changed from 0 to 1 for the <> operator

cat's stdin ist sowohl zum Lesen als auch zum Schreiben geöffnet, was bedeutet, dass cates niemals eof darauf sehen wird (es instanziiert auch die Pipe sofort, selbst wenn es keinen anderen Prozess gibt, der das fifozum Schreiben öffnet ).

Auf Systemen, auf denen dies nicht funktioniert, können Sie stattdessen Folgendes tun:

cat < fifo 3> fifo

Auf diese Weise wird, sobald ein anderer Prozess das fifoSchreiben öffnet , der erste schreibgeschützte Vorgang open()zurückgegeben. An diesem Punkt führt die Shell open()vor dem Start den schreibgeschützten Vorgang aus cat, wodurch verhindert wird, dass die Pipe jemals wieder zerstört wird.

Um es zusammenzufassen:

  • im Vergleich dazu cat filewürde es nach der ersten Runde nicht aufhören.
  • im vergleich zu tail -n +1 -f file: es würde nicht read()jede sekunde nach der ersten runde nutzlos machen , es würde niemals eof auf der einen instanz der pipe geben, es würde nicht bis zu einer sekundenverzögerung geben, wenn ein zweiter prozess die rohre zum schreiben nach der runde öffnet zuerst hat man es geschlossen.
  • im Vergleich zu tail -f file. Darüber hinaus müsste es nicht warten, bis die erste Runde beendet ist, bevor etwas ausgegeben wird (nur die letzten 10 Zeilen).
  • Im Vergleich zu cat fileeiner Schleife würde es nur eine Pipe-Instanz geben. Die in ¹ genannten Rennfenster würden vermieden.

¹ An diesem Punkt, zwischen dem letzten read(), der eof anzeigt, und dem catBeenden und Schließen des Leseendes der Pipe, gibt es tatsächlich ein kleines Fenster, in dem ein Prozess das fifozum erneuten Schreiben öffnen könnte (und nicht blockiert werden kann, da es noch ein Leseende gibt ). Wenn es dann etwas schreibt, nachdem es beendet catwurde und bevor ein anderer Prozess das fifozum Lesen öffnet , wird es mit einem SIGPIPE getötet.

Stéphane Chazelas
quelle
genial. Sie sind gut darin, Dinge zu erklären. über cat <>fifo: cat will never see eof on it. Ich verstehe, dass die Instanz offen bleibt. Aber wenn ein anderer Prozess einen EOF darauf schreibt, sollte er auch eintreffen cat, oder? Es wird also lieber ignoriert, da bidirektionale Rohrleitungen vorhanden sind. Vielen Dank!
Phil294
1
@Blauhirn, es gibt kein "Schreiben eines EOF in eine Pipe" . Sie sehen EOF auf einer Pfeife, wenn alle Autoren weg sind, Punkt. Vielleicht denken Sie an die ^DTermios-Einstellung. Dies gilt jedoch nur für E / A-Vorgänge an Endgeräten, dh an Geräte mit seriellen / Pty-Zeichen, denen eine Tty-Line-Disziplin zugeordnet ist (und dies nur in bestimmten Betriebsarten), keine Pipes, keine Socket-Paare, keine regulären Dateien, keine Verzeichnisse , keine Geräte blockieren, keine anderen Zeichengeräte.
Stéphane Chazelas
1
@Blauhirn (Fortsetzung): Wenn Sie das Fifo im Lese- / Schreibmodus öffnen, haben wir ein fd an beiden Enden der Pipe (nicht bidirektional; selbst bei Systemen, bei denen reguläre Pipes bidirektional sind, haben benannte Pipes nur eine Richtung) Immer ein fd am anderen Ende (das gleiche fd wie das, von dem wir lesen), wir werden nie eof sehen, da es am anderen Ende immer noch einen Schriftsteller gibt: uns.
Stéphane Chazelas
2

Lassen Sie mich eine andere Lösung vorschlagen. Pipe steht zum Lesen zur Verfügung, solange ein Prozess am zweiten Ende schreibt. So können Sie catim Hintergrund (oder in einem anderen Terminal) eine Fälschung erstellen , zum Beispiel:

mkfifo fifo
cat >fifo &
cat fifo

Jetzt können Sie so lange an fifo schreiben, wie Sie möchten, und wenn Sie fertig sind, töten Sie einfach den Strom catmit C-cund fgbringen Sie ihn dann zuerst cataus dem Hintergrund und schließlich C-d, um ihn zu stoppen.

jimmij
quelle
1
In POSIX-Shells und wenn Sie nicht interaktiv sind und ein Befehl im Hintergrund gestartet wird /dev/null, catwird sein Standardwert so, dass er unmittelbar nach dem Start beendet wird. In Shells, die dies nicht tun und / oder von einer interaktiven Shell in einem Terminal ausgeführt werden, catwird sie wahrscheinlich angehalten, wenn versucht wird, vom Terminal zu lesen, ohne im Vordergrund zu stehen.
Stéphane Chazelas