![]() |
Sperren von Dateien: Typische Fehler beim Sperren von Dateien |
|
| |
Es gibt einige typische Fehler beim Sperren von Dateien, die immer wieder gemacht werden. Dieser Abschnitt erläutert diese Fehler und erklärt die Hintergründe. In den nächsten Abschnitten wird dann gezeigt, wie das Sperren von Dateien richtig funktioniert.
Wird eine Datei zum Schreiben geöffnet, so wird der Dateiinhalt in diesem Moment automatisch gelöscht! Jeder Aufruf einer Funktion, die die Datei sperren soll, kommt zu spät für eventuell parallel lesende Zugriffe. Daher ist es essentiell, dass eine Datei nicht zum Schreiben geöffnet wird. In folgender Tabelle sind Codeausschnitte, wie sie typischerweise in Programmiersprachen verwendet werden, um Dateien zum Schreiben zu öffnen. Diese Codeausschnitte sollten nicht verwendet werden, sie sind als Negativbeispiele aufgeführt!
| Sprache | Code (Negativbeispiel!) |
|---|---|
| PHP | $fp = fopen ('datei', 'w'); |
| Perl | open FP, ">", "datei"; |
| Python | fp = open ('datei', 'w') |
| Java | stream = new FileOutputStream ("datei"); |
Welche Abhilfe gibt es? Die einfachste Möglichkeit ist, die Datei zum Lesen und Schreiben zu öffnen (hauptsächlich jedoch zum Lesen, Dateimodi wie w+ in PHP/Python oder >+ in Perl sind auch nicht sinnvoll!) und dann nach Erhalt der Dateisperre den Dateiinhalt zu löschen. Weiter unten im Artikel werden die korrekten Lösungen für die hier beschriebenen Programmiersprachen angeführt und erläutert.
Erinnert man sich an den Zähler, der in der Einführung des Artikels vorgestellt wurde, gibt es auch hier Fälle, in denen selbst bei Benutzung von Dateisperren es zu inkorrekten Ergebnissen kommen kann - wenngleich die Information in der Datei selbst nicht verloren gehen kann. Der Zähler sei im folgenden Szenario nicht wie oben implementiert, sondern in zwei Teile geteilt:
Sofern das Öffnen und Sperren korrekt implementiert ist, sieht dies erst einmal formal korrekt aus. Es gibt jedoch auch hier eine race condition:
Wie man hier erkennen kann, ist ein Aufruf zu wenig gezählt worden, tatsächlich hätte 44 in die Datei geschrieben werden sollen. Das ist hier im Falle des Zählers nicht gravierend, bei anspruchsvolleren Programmen kann dieses Verhalten jedoch durchaus ernste Probleme verursachen.
Der Ausweg hierzu: Wenn ein Programm oder Script auf den Inhalt der Datei direkt sofort reagieren muss, dann muss es die Datei bereits vor dem Lesen exklusiv sperren, die Sperre aufrecht erhalten, solange der Inhalt verarbeitet wird, den Dateiinhalt danach löschen und den neuen Inhalt in die Datei schreiben, und danach erst die Dateisperre aufheben. Denn nur so ist sichergestellt, dass der Zugriff auf die Datei vollkommen isoliert war. Auch hier wird der entsprechend korrekte Code weiter unten vorgestellt.
Oftmals wird vor dem Schließen der Dateien die Dateisperre aufgehoben. Dies kann problematisch sein, denn meistens wird gepufferte Ein- und Ausgabe verwendet, d.h. die Ausgabe in eine Datei wird im Speicher vorgehalten, um sie später - spätestens beim Schließen der Datei - tatsächlich schreiben zu können. Falls nun jedoch ein Schreibbefehl gepuffert (d.h. effektiv noch nicht ausgeführt wird), dann die Sperre entfernt und danach erst die Datei geschlossen wird, führt dies dazu, dass der Schreibbefehl erst beim Schließen der Datei ausgeführt wird, wenn die Sperre schon längst entfernt wurde. Da Sperren beim Schließen der Datei automatisch entfernt werden, ist es sinnvoller, die Datei einfach nur zu schließen, ohne sich um die Sperre explizit zu kümmern.
Nur wer explizit die Datei offen halten will, sollte Sperren manuell entfernen - dafür aber vorher mit geeigneten Funktionen sicherstellen, dass alle Puffer geleert sind, d.h. dass alle Schreiboperationen auf die Datei erfolgt sind.