Webbasierte Adressdatenbank
File Locking, warum?
Wie wir in Webserver eine Einführung gesehen haben, können
mehrere Anfragen an einen httpd zur gleichen Zeit gestellt werden. Dies bedeutet für
unsere Adressdatenbank, daß z. B. ein Benutzer aus dem Datenfile liest, während ein
anderer schreibend darauf zugreift. Mit an Sicherheit grenzender Wahrscheinlichkeit wird
dies schief gehen, am Schluß bleibt ein zerstörtes Datenfile zurück. Wir müssen also
Maßnahmen ergreifen um dieses Verhalten zu unterbinden, das sogenannte "file
locking" oder flock.
Um ein flock erfolgreich durchzuführen, bedarf es allerdings der Ünterstützung
des Betriebssystemes. Linux und Unix bieten diese Unterstützung, Windows nicht.
Wie wird ein File gelockt?
Locks gibt es in zwei Varianten, shared und exclusiv. Den shared Lock verwendet man
normaler Weise zum Lesen. Ein so gelocktes File kann von weiteren Prozessen ebenso mit
einem shared Lock versehen werden. Wenn man in eine Datei schreiben will, verwendet man
den exclusiv Lock. Fordert ein weiterer Prozess einen exclusiv, oder shared Lock an, wird
er kein Erfolg haben. D. h. der Prozess mit exclusiv Lock kann schreiben ohne das etws
schief geht.
Allerdings funktioniert das Ganze nur, wenn alle Prozesse, die mit dem File arbeiten
den Lock Mechanismus verwenden.
Beispiel locksh.pl
1: #!/usr/bin/perl -w 2: 3: use strict; 4: use Fcntl qw/:flock/; # Importiert Lock Konstanten 5: 6: my $dat_file = "daten.txt"; 7: my $num; 8: 9: open (DAT , "<$dat_file") || die "Kann file $dat_file: nicht öffnen: $!"; 10: 11: flock(DAT, LOCK_SH) || die "Fehler beim Locken: $!"; 12: 13: print "File zum Lesen gelockt\n"; 14: chomp($num = <DAT>); 15: print $num."\n"; 16: sleep 15; 17: 18: close(DAT) || die "Kann File $dat_file nicht schliesen: $!"; 19: 20: print "File unlocked!\n";
In der Zeile 4 importieren wir die Lock Konstanten aus dem Standard Modul
Fcntl
. Nach dem Öffnen der Datei zum Lesen setzen wir den shared
Lock in der Zeile 11. Ein explizites Unlock wird nicht durchgeführt, dies wird beim
close
in Zeile 18 automatisch gemacht. In Zeile 16 warten wir 15
Sekunden bis es weitergeht. Das gibt uns die Möglichkeit das Skript in einer weitern Shell
erneut aufzurufen und zu sehen was passiert.
Beispiel lockex.pl
1: #!/usr/bin/perl -w 2: 3: use strict; 4: use Fcntl qw/:flock/; # Importiert Lock Konstanten 5: 6: my $dat_file = "daten.txt"; 7: my $num; 8: 9: open (DAT , "+<$dat_file") || die "Kann File $dat_file nicht öffnen: $!"; 10: 11: flock (DAT, LOCK_EX|LOCK_NB) || &fehler(1); 12: flock (DAT, LOCK_EX) || &fehler(2); 13: 14: print "File zum schreiben und lesen gelocked\n"; 15: chomp($num = <DAT>) || 0; 16: print $num."\n"; 17: seek(DAT, 0, 0) || die "Kann nicht an den Anfang der Datei spulen: $!"; 18: truncate(DAT, 0) || die "Kann File nicht abschneiden: $!"; 19; print DAT $num+1, "\n"; 20: sleep 15; 21: close(DAT) || die "Kann File $dat_file nicht schliesen: $!"; 22: print "File unlocked!\n"; 23: 24: sub fehler { 25: my $error = @_; 26: my %fehlertext = (1 => 'Kein sofortiger Schreib Lock möglich!', 27: 2 => 'Fehler beim Schreib Lock!'); 28: print "$fehlertext{$error}\n"; 29: exit; 30: }
Hier öffnen wir das File zum Lesen und Schreiben. In Zeile 11 versuchen wir zuerst
einen nonblocking Lock, denn, wird ein exclusiv Lock gesetzt, und ein anderer Prozess
versucht einen weiteren exclusiv Lock zu setzten muß der warten, bis der erste Lock
aufgehoben ist. Der nonblocking Lock gibt uns die Möglichkeit in eine Fehlerroutine zu
gelangen, falls der exclusiv Lock fehlschlägt. Dann Lesen wir eine Zeile des Files ein,
"spulen zum Anfang der Datei" zurück (Zeile 17). Danach leeren wir das File (Zeile18),
erhöhen den Zähler und schreiben ihn zurück. Auch hier löschen wir den Lock nicht
explizit, da gegebenenfalls ja ein Schreibpuffer aktiv ist. Das würde dazu führen, daß ein
anderer Prozess schreibt, bevor wir mit close
den Schreibpuffer
in die Datei schreiben.
Literaturhinweise:
- perldoc perlfunc
- perldoc perlfaq5
Zurück zum Anfang dieses Projekts.