C++ Ist das Lock-File Konzept sicher?

T_55

Lieutenant
Registriert
Feb. 2013
Beiträge
643
Hallo,

zwei Programme sollen auf ein txt File zugreifen können. Wie kann man das am besten Synchronisieren um Fehler bei gleichzeitigem Zugriff zu verhindern (ich denke mal das könnte sonst zu einem Problem führen). Suche eine Cross-Platform Lösung.

Idee: Ich habe in Erinnerung, dass sowas per Lock-File gehen kann. Die Programme schauen also erst ob eine Lockfile (file.lock) vorhanden ist bevor ein Zugriff auf die eigentliche Datei stattfindet.

Wenn Lockfile nicht vorhanden:
dann wird Lockfile erstellt und auf eigentliche Datei (file.txt) zugegriffen, wenn fertig wird lockfile gelöscht.

Wenn Lockfile vorhanden:
dann warte kurz und versuche erneut.

So ein Konzept wird auch hier vorgeschlagen: https://stackoverflow.com/questions...simultaneous-read-and-write-lock?noredirect=1

Es gibt wohl auch sowas wie boost::interprocess::file_lock aber da würde ich viel lieber einen "Keep it simple" Weg gehen als mit IPC zu spielen...

Spricht etwas gegen das Lock-File Konzept?

Gruß
 
Prinzipiell find ich die Idee nicht schlecht. Habe vor einiger Zeit etwas ähnliches mit C# gebaut für eine Webserver Log-Geschichte. Was ich noch gemacht habe, ich habe die Datei immer mit direktem Schreibzugriff geöffnet, zumindest hat Windows die Datei sofort geblockt für andere Prozesse.
 
Das einzig nervige ist, wenn ein Prozess abstürzt und das Lock-File nicht löscht. Oder bspw. solche DropBox-Sync-Clients auch die Lock-Files mit kopieren.

Wenn es auf dem gleichen System stattfindet, tut es eine Exklusiv-geöffnete Datei besser. Das müsste sogar über SMB-Netzlaufwerke funktionieren.
 
Sofern es sich immer auf dem selben System abspielt, schau dir mal "named Mutex" und "named Semaphore" an.
 
Zuletzt bearbeitet:
Bagbag schrieb:
Sofern es sich immer auf dem selben System abspielt, schau dir mal "named Mutex" und "named Semaphore" an.

Das wäre vermutlich die sauberere Lösung. Dadurch wird verhindert dass das Programm in einen kritischen codeabschnitt eintaucht solange das entsprechende mutex gesetzt ist. Nutze aber die Klasse lock_guard... sie hilft auch dabei nicht zu vergessen das mutex wieder freizugeben falls du unerwartet aus dem codeabschnitt heraus musst
 
Wie wuerde man denn lock guards auf mutexes ueber Prozessgrenzen hinweg einsetzen?
 
std::shared_mutex geht auch nicht über Prozessgrenzen oder? (Wäre allerdings eine Alternative zu atomaren Spinlocks da es wie es aussieht über Threadgrenzen geht (im Gegensatz zu normalen Mutexen)).
 
Kaum nimmt einer das Wort Mutex in den Mund, wird zielsicher am ursprünglichen Problem vorbei geredet :evillol:

Spricht etwas gegen das Lock-File Konzept?
Wichtig bei dem Konzept ist halt, dass die Operation "Prüfen ob vorhanden, und wenn nicht, erstellen" atomar ist. Und spätestens da braucht man glaube ich ohnehin plattformspezifischen Code. Alternativ gibt es auch flock unter Linux und LockFile unter Windows, vielleicht die etwas sauberere Lösung.

T_55 schrieb:
(Wäre allerdings eine Alternative zu atomaren Spinlocks da es wie es aussieht über Threadgrenzen geht (im Gegensatz zu normalen Mutexen)).
Was meinst du damit? Der Unterschied zwischen std::shared_mutex und std::mutex ist, dass shared_mutex mehrere Reader erlaubt, während mutex keine Unterscheidung zwischen Reader und Writer macht. Die Alternative zu einfachen Spinlocks ist std::mutex.
 
Es gibt wohl auch sowas wie boost::interprocess::file_lock aber da würde ich viel lieber einen "Keep it simple" Weg gehen als mit IPC zu spielen...
​Ich denke nicht, dass hier Interprozesskommunikation in dem Sinn wie du denkst benutzt wird. Es heißt immer noch file_lock. Benutz es einfach. Die API ist jetzt nicht so kompliziert.

Wichtig bei dem Konzept ist halt, dass die Operation "Prüfen ob vorhanden, und wenn nicht, erstellen" atomar ist.
​Nicht direkt, oder?
Wenn die Datei zunächst nicht vorhanden ist, aber dann erstellt wird, nachdem du das geprüft hast, dann ist tatsächlich eine Datei vorhanden. Versuchst du die Datei nun zu erstellen, schlägt das erstellen ganz einfach fehl.
​Und solange Datei erstellen vom Betriebsystem selbst atomar erledigt wird, ist man auf der sicheren Seite mMn. Von daher könnte man das schon so machen.
 
Zuletzt bearbeitet:
The Ripper schrieb:
​Versuchst du die Datei nun zu erstellen, schlägt das erstellen ganz einfach fehl.
Genau das Verhalten meine ich eigentlich mit atomar, aber dafür muss man auch Schnittstellen benutzen, bei denen das Erstellen einer existierenden Datei tatsächlich fehlschlägt. Das ist bei sowas wie std::ofstream AFAIK nicht der Fall.

Auch bei den Posix-APIs muss man das Verhalten explizit erzwingen, sonst passiert einfach gar nichts:
Code:
int fd = open("file.lock" O_CREAT | O_EXCL);
 
Ja vermutlich wäre plattformspezifisch am saubersten.

VikingGe schrieb:
Was meinst du damit? Der Unterschied zwischen std::shared_mutex und std::mutex ist, dass shared_mutex mehrere Reader erlaubt, während mutex keine Unterscheidung zwischen Reader und Writer macht. Die Alternative zu einfachen Spinlocks ist std::mutex.

Ich meinte nur weil beim normale Mutex das Lock und Unlock im selben Thread stattfinden muss. Ich hatte mal den Fall, dass das Lock und Unlock in unterschiedlichen Threads stattfinden sollte. Ich hatte dann per std::atomic<bool> und der Abfrage lockvar.exchange(true) ein Threadübergreifenden Lock gebaut. Scheinbar kann man auch einfach shared_mutex nehmen das war nur meine neue Offtopic Erkenntnis.

The Ripper schrieb:
​Ich denke nicht, dass hier Interprozesskommunikation in dem Sinn wie du denkst benutzt wird. Es heißt immer noch file_lock. Benutz es einfach. Die API ist jetzt nicht so kompliziert.

Scheinbar ist es gar kein echter File-Lock so wie es die Bezeichnung andeutet: https://stackoverflow.com/questions...le-lock-does-not-work-with-multiple-processes
"File_lock is not for locking a file"


Ein File-Lock per Lock-Datei habe ich inzwischen erstellt.
verkürzt so:

prüfe ob Lockfile existiert:
Code:
std::ifstream f(name.c_str());
if(f.good() == 0) // existiert nicht
{
    // erstelle file:
   std::ofstream myfile {name};
   myfile.close();
}
else // existiert
{
    f.close();
}

und löschen:

Code:
std::remove(name.c_str());


Das klappt soweit sehr gut ausser beim Härtetest. Der Härtetest ist aber auch ein unrealistischer Fall der in meinem Usecase nicht so auftreten würde. Dabei versuchen 3 Programme gleichzeitig jeweils mehrere Hundert Einträge in die selbe Datei zu schreiben. Dann fehlen manchmal Einträge oder eines der Programme kann die Lockfile nicht löschen und bleibt dabei in einer Schleife stecken. Bei nur zwei Programmen funktioniert die Sache wiederum Fehlerfrei (was meinem Usecase entspricht).
Scheinbar spielt close() eine große Rolle für die jeweils konkurierenden Prozesse denn ohne close() scheint die Abfrage f.good in einem anderen Prozess fehlerhaft bzw wird als existierend erkannt obwohl es bereits gelöscht ist. Dies hab ich aber auch bisher nur in Windows getestet, vielleicht gibt es unter Linux wieder ein anderes Verhalten. Aber für Zugriffe hin und wieder klappt es so schon mal.
 
Scheinbar ist es gar kein echter File-Lock so wie es die Bezeichnung andeutet:
https://stackoverflow.com/questions/...iple-processes
"File_lock is not for locking a file"
​Der meint eher, dass man mit file locking nicht verhindert, dass irgendjemand anderes, der die Datei "filename.lock" ignoriert, die Datei "filename" trotzdem bearbeitet. Es fungiert nur als Flag, und keiner zwingt einem das zu nutzen.

​EDIT: Vgl. Frage zur Antwort ;)
 
Zuletzt bearbeitet:
Zurück
Oben