2016-09-20 3 views
0

Im Prinzip was ich will ist sehr einfach.Linux Synchronisation ohne Polling

Zwei ausführbare Dateien ./read und ./write bzw. Lesen und Schreiben von einer Ressource (sagen wir eine Datei). Mit flock(2) ist es einfach, zu willkürlichen Zeiten Race-Bedingungen zwischen beliebigen Aufrufen von ./read und ./write zu verhindern.

Die Anforderung besteht darin, daß jeder Aufruf von ./read enthält eine Momentaufnahme der Ressource von einem frühen Aufruf, und, wenn die aktuelle Ressource des Schnappschuss übereinstimmt, sollte warten ./read (sleep), bis ein Aufruf von ./write die Ressource ändert.

Von dem, was ich sammeln, sollten die Programmabläufe der einzelnen Programme werden so etwas wie:

//read.c 
obtain mutex0 
    read resource 
    is resource same as our snapshot? 
    release mutex0 [1] 
    sleep until ./write says to wake up [2] 
    obtain mutex0 
    read resource 
    do something with resource 
release mutex0 

//write.c 
obtain mutex0 
    change resource in some way 
    tell any sleeping ./read's to wake up 
release mutex0 

Das Hauptproblem bei diesem Ansatz ist, dass es zwischen den Zeilen markiert [1] und [2] eine spürbare Verzögerung. Das bedeutet, dass ein ./readmutex0 bei [1], eine ganze Aufruf von ./write abschließen können freigeben kann, und dann [2] ausführt, wird aber Stall auf unbestimmte Zeit, weil ./write bereits versucht, bevor Sie schlafen ./read s aufzuwachen.

Gibt es keine einfache Möglichkeit, das zu tun, was ich möchte, abgesehen von der Verwendung eines kompletten separaten Serverprozesses? Auch für die Neugierigen möchte ich dies für eine Anwendung in CGI tun.

+1

Sie könnten einen Semaphor anstelle eines Mutex verwenden. – Riley

+0

Es scheint mir, dass Sie Interprozesskommunikation wollen. Ich schlage vor, auf linux Named Pipes oder [ZeroMQ] (http://czmq.zeromq.org/) zu lesen, anstatt auf Ihrem Ad-hoc-Schema. – orlp

+0

Keiner Ihrer Kommentare adressierte die von mir aufgezeigte Race Condition. Ich weiß von Semaphoren, ich weiß von IPC. –

Antwort

1

Nein, der Programmablauf für das Lesegerät ist nicht korrekt. Sie benötigen eine Art Sperrmechanismus, um Schreibvorgänge zu verhindern, während eine oder mehrere Lesevorgänge ausgeführt werden, sowie eine Art Wakeup-Mechanismus, um Leser zu benachrichtigen, wenn ein Schreibvorgang abgeschlossen ist.

Ihr Programmablauf für den Autor (en) ist in Ordnung:

# Initial read of file contents 
    Obtain lock 
     Read file 
    Release lock 

    # Whenever wishes to modify file: 
    Obtain lock 
     Modify file 
     Signal readers 
    Release lock 

Der Programmablauf für den Leser (s) sollte sein:

# Initial read of file contents 
    Obtain lock 
     Read file 
    Release lock 

    # Wait and respond to changes in file 
    On signal: 
     Obtain lock 
      Read file 
     Release lock  
     Do something with modified file contents 

Wenn es nur eine Leser, dann genügt ein Mutex (pthread_mutex_t) im geteilten Speicher (zugänglich für alle Schreiber und den Leser); Ansonsten empfehle ich stattdessen einen Rwlock (pthread_rwlock_t). Zum Aufwecken von wartenden Lesern wird eine Zustandsvariable gesendet (pthread_cond_t). Die Schwierigkeit besteht natürlich darin, diesen gemeinsamen Speicher einzurichten.


Advisory-Datei sperren und die fanotify Schnittstelle ist auch ausreichend. Leser installieren ein fanotify FAN_MODIFY markieren, und warten Sie einfach auf das entsprechende Ereignis. Autoren müssen nicht zusammenarbeiten, mit Ausnahme der Verwendung einer Advisory-Sperre (die nur dazu dient, Lesern das Lesen zu verweigern, während die Datei geändert wird).

Leider benötigt die Schnittstelle derzeit die CAP_SYS_ADMIN Fähigkeit, die Sie nicht wollen, dass zufällige CGI-Programme haben.


Advisory Dateisperren und die inotify Schnittstelle genügt, und ich glaube, die am besten geeignet für diesen, wenn beiden Leser und Schreiber öffnen und die Datei für jeden Satz von Operationen schließen. Der Programmablauf für diesen Fall für den Leser (n):

Initialize inotify interface 
Add inotify watch for IN_CREATE and IN_CLOSE_WRITE for "file" 

Open "file" read-only 
    Obtain shared/read-lock 
     Read contents 
    Release lock 
Close "file" 

Loop: 
    Read events from inotify descriptor. 
    If IN_CREATE or IN_CLOSE_WRITE for "file": 
     Open "file" read-only 
      Obtain shared/read-lock 
       Read contents 
      Release lock 
     Close "file" 
     Do something with file contents 

Der Schriftsteller gerade noch ist

# Initial read of file contents 
    Open "file" for read-only 
     Obtain shared/read-lock on "file" 
      Read contents 
     Release lock 
    Close "file" 

    # Whenever wishes to modify file: 
    Open "file" for read-write 
     Obtain exclusive/write-lock 
      Modify file 
     Release lock 
    Close "file" 

Auch wenn die Autoren nicht in das Schloss erhalten, werden die Leser, wenn eine Benachrichtigung Writer schließt die Datei; Das einzige Risiko besteht darin, dass ein anderer Satz von Änderungen geschrieben wird (durch einen anderen Modifizierer zum Sperren und Zurückweisen), während die Leser die Datei lesen.

Selbst wenn ein Modifizierer die Datei durch eine neue ersetzt, werden die Leser korrekt benachrichtigt, wenn eine neue bereit ist (entweder umbenannt/über die alte verknüpft oder der neue Dateiersteller schließt die Datei). Es ist wichtig zu beachten, dass, wenn die Leser die Datei offen halten, ihre Dateideskriptoren nicht auf magische Weise zu der neuen Datei springen, und sie werden nur die alten (wahrscheinlich gelöschten) Inhalte sehen.


Wenn es aus irgendeinem Grunde ist wichtig, dass Leser und Schreiber nicht schließen Sie die Datei, die Leser noch inotify verwenden können, aber ein IN_MODIFY Zeichen stattdessen benachrichtigt zu werden, wenn die Datei auf abgeschnitten oder geschrieben ist. In diesem Fall ist es wichtig, sich daran zu erinnern, dass die Leser und Schreiber die neue Datei nicht sehen werden, wenn die Datei dann ersetzt (umbenannt oder gelöscht und neu erstellt) wird, aber auf der alten, jetzt unsichtbar-in-der-Datei ausgeführt wird Dateiinhalte.

Der Programmablauf für den Leser:

Initialize inotify interface 
Add inotify watch for IN_MODIFY for "file" 

Open "file" read-only 
    Obtain shared/read-lock 
     Read contents 
    Release lock 

    Loop: 
     Read events from inotify descriptor. 
     If IN_CREATE or IN_CLOSE_WRITE for "file": 
      Obtain shared/read-lock on "file" 
       Read contents 
      Release lock 
      Do something with file contents 

Der Programmablauf für den Schriftsteller ist immer noch fast das gleiche:

# Initial read of file contents 
    Open "file" for read-only 
     Obtain shared/read-lock on "file" 
      Read contents 
     Release lock 
    Close "file" 

    Open "file" for read-write 

    # Whenever writer wishes to modify the file: 
    Obtain exclusive/write-lock 
     Modify file 
    Release lock 

Es kann wichtig sein, zu beachten, dass die inotify Ereignisse treten nach der Tat auf. Es gibt normalerweise eine kleine Latenz, die von der Auslastung der Maschine abhängen kann. Wenn eine schnelle Antwort auf Dateiänderungen wichtig ist, damit das System ordnungsgemäß funktioniert, müssen Sie möglicherweise stattdessen eine Mutex- oder Rwlock- und eine Bedingungsvariable im Shared Memory-Ansatz verwenden.

Nach meiner Erfahrung sind diese Latenzen tendenziell kürzer als das typische menschliche Reaktionsintervall. Daher denke ich - und ich schlage vor, Sie tun es auch - die Inotify-Schnittstelle schnell und zuverlässig genug zu menschlichen Zeitskalen; Nicht so bei Millisekunden- und Sub-Millisekunden-Maschinen-Zeitskalen.