2015-02-04 14 views
7

Gibt es eine Möglichkeit für einen Autor zu wissen, dass ein Leser sein Ende einer Named Pipe (oder Exited), ohne geschrieben hat, um es zu schreiben?Erkennen, wenn der Leser die Named Pipe (FIFO) schließt

Ich muss das wissen, weil die Anfangsdaten, die ich in die Pipe schreibe, unterschiedlich sind; Der Leser erwartet einen ersten Header, bevor der Rest der Daten kommt.

Momentan erkenne ich dies, wenn mein write() mit EPIPE ausfällt. Ich habe dann eine Flagge gesetzt, die sagt "nächstes Mal, sende den Header". Es ist jedoch möglich, dass der Leser das Rohr schließt und wieder öffnet, bevor ich irgendetwas geschrieben habe. In diesem Fall merke ich nie, was er getan hat, und sende nicht den Header, den er erwartet.

Gibt es irgendeine asynchrone Ereignisart, die hier helfen könnte? Ich sehe keine Signale gesendet werden.

Beachten Sie, dass ich keine Sprach-Tags eingefügt habe, da diese Frage als sprachunabhängig betrachtet werden sollte. Mein Code ist Python, aber die Antworten sollten für C oder jede andere Sprache mit Bindungen auf System-Aufrufebene gelten.

+0

man 2 schreiben: 'EPIPE'' fd ist an ein Rohr oder eine Steckdose angeschlossen, deren Leseende geschlossen ist. Wenn dies geschieht, erhält der Schreibprozess auch ein SIG- PIPE-Signal.(Daher wird der Write-Rückgabewert nur angezeigt, wenn das -Programm dieses Signal abfängt, blockiert oder ignoriert.) ' – wildplasser

+0

Gibt es einen Grund, die Headerdaten nicht regelmäßig zu senden? Wenn der Leser damit umgehen kann, kann er vielleicht mit redundanten Headern umgehen? – wallyk

+0

@wildplaser Ich bin mir nicht sicher, welchen Punkt du gerade machst. Ja, ich weiß, dass die Pipe geschlossen ist, wenn ich versuche, darauf zu schreiben. Meine Frage lautet, wie man dies asynchron erkennt, ohne zu schreiben. –

Antwort

2

Merkwürdigerweise scheint es, dass, wenn das letzte Lesegerät das Rohr schließt, select zeigt an, dass das Rohr lesbar ist:

writer.py

#!/usr/bin/env python 
import os 
import select 
import time 

NAME = 'fifo2' 

os.mkfifo(NAME) 


def select_test(fd, r=True, w=True, x=True): 
    rset = [fd] if r else [] 
    wset = [fd] if w else [] 
    xset = [fd] if x else [] 

    t0 = time.time() 
    r,w,x = select.select(rset, wset, xset) 

    print 'After {0} sec:'.format(time.time() - t0) 
    if fd in r: print ' {0} is readable'.format(fd) 
    if fd in w: print ' {0} is writable'.format(fd) 
    if fd in x: print ' {0} is exceptional'.format(fd) 

try: 
    fd = os.open(NAME, os.O_WRONLY) 
    print '{0} opened for writing'.format(NAME) 

    print 'select 1' 
    select_test(fd) 

    os.write(fd, 'test') 
    print 'wrote data' 

    print 'select 2' 
    select_test(fd) 

    print 'select 3 (no write)' 
    select_test(fd, w=False) 

finally: 
    os.unlink(NAME) 

Demo:

Terminal 1:

$ ./pipe_example_simple.py 
fifo2 opened for writing 
select 1 
After 1.59740447998e-05 sec: 
3 is writable 
wrote data 
select 2 
After 2.86102294922e-06 sec: 
3 is writable 
select 3 (no write) 
After 2.15910816193 sec: 
3 is readable 

Terminal 2:

$ cat fifo2 
test 
# (wait a sec, then Ctrl+C) 
3

Wenn Sie eine Ereignisschleife verwenden, die auf dem Systemaufruf poll basiert, können Sie die Pipe mit einer Ereignismaske registrieren, die EPOLLERR enthält. In Python, mit select.poll,

import select 
fd = open("pipe", "w") 
poller = select.poll() 
poller.register(fd, select.POLLERR) 
poller.poll() 

wird warten, bis das Rohr geschlossen ist.

Um dies zu testen, führen Sie mkfifo pipe, starten Sie das Skript, und in einem anderen Terminal ausgeführt, z. B. cat pipe. Sobald Sie den Prozess cat beenden, wird das Skript beendet.

+0

Was ist 'Poller' hier? –

+0

Sory, ich habe vergessen, diese Zeile hinzuzufügen. Es ist ein 'select.poll'-Objekt. – Phillip

+0

Das zweite Argument sollte 'select.POLLERR' sein, nicht' select.EPOLLERR'. Hast du das auch probiert? Mein Test mit 'select' ([unten] (http://stackoverflow.com/a/28472725/119527)) zeigt an, dass die Pipe überraschenderweise * lesbar *, nicht * außergewöhnlich * wird, wenn das andere Ende geschlossen ist. –

1

Es gibt keinen solchen Mechanismus. Im Allgemeinen gibt es gemäß dem UNIX-Weg keine Signale für das Öffnen oder Schließen von Strömen an jedem Ende. Dies kann nur durch Lesen oder Schreiben (entsprechend) festgestellt werden.

Ich würde sagen, das ist falsches Design. Derzeit versuchen Sie, dass der Empfänger seine Verfügbarkeit signalisieren kann, indem er eine Pipe öffnet. Entweder implementieren Sie diese Signalisierung in geeigneter Weise oder integrieren die "Schlusslogik" in den sendenden Teil der Pipe.

+0

'" Es gibt keinen solchen Mechanismus. "" Hast du meine Antwort oben gesehen, die deutlich zeigt, dass du falsch liegst? –

+0

Die Anrufe "Poll" und "Select" werden verwendet, um zu erkennen, wann ein FD zum Lesen/Schreiben bereit ist, um Blockierungen zu vermeiden. Es gibt immer noch keine Signalisierungsinformationen zum Öffnen oder Schließen von Streams. Um dies zu erkennen, müssen Sie einen Lese-/Schreibzugriff oder einen Test für Lese-/Schreibbereitschaft durchführen. Ich gebe zu, dass diese Information weggelassen wird. Die Antwort ändert sich trotzdem nicht, da Polling immer noch kein Signalisierungsmechanismus ist und die von Ihnen beschriebenen Race Conditions (das Pipe Closing und Re-Opening vor dem Polling/Reading/Write) ist immer noch mit 'poll' vorhanden. Es könnte wieder geöffnet werden, bevor Sie es abfragen. –

Verwandte Themen