2008-08-29 18 views
25

Was ist der beste Weg, um eine Sperre für eine Datei in Perl zu erstellen?Wie sperre ich eine Datei in Perl?

Ist es am besten, die Datei zu verschmelzen oder eine Sperrdatei zu erstellen, um eine Sperre zu aktivieren und nach einer Sperre für die Sperrdatei zu suchen?

+1

Seite beachten, wie Perl schreiben: http://stackoverflow.com/questions/72312/how-should-i-capitalize- perl – szabgab

+0

nicht sicher, ob die Perl-Jungs hier sind Lily zu tun, eine "faq" wie C++ hat, aber ich würde sagen, das ist ein schönes Beispiel für eine – thecoshman

Antwort

27

tragbar Wenn Sie Herde am Ende mit, hier ist etwas Code, es zu tun:

use Fcntl ':flock'; # Import LOCK_* constants 

# We will use this file path in error messages and function calls. 
# Don't type it out more than once in your code. Use a variable. 
my $file = '/path/to/some/file'; 

# Open the file for appending. Note the file path is quoted 
# in the error message. This helps debug situations where you 
# have a stray space at the start or end of the path. 
open(my $fh, '>>', $file) or die "Could not open '$file' - $!"; 

# Get exclusive lock (will block until it does) 
flock($fh, LOCK_EX) or die "Could not lock '$file' - $!"; 

# Do something with the file here... 

# Do NOT use flock() to unlock the file if you wrote to the 
# file in the "do something" section above. This could create 
# a race condition. The close() call below will unlock the 
# file for you, but only after writing any buffered data. 

# In a world of buffered i/o, some or all of your data may not 
# be written until close() completes. Always, always, ALWAYS 
# check the return value of close() if you wrote to the file! 
close($fh) or die "Could not write '$file' - $!"; 

einige nützliche Links:

Als Antwort auf Ihre Frage, würde ich sagen, entweder die Sperre auf die Datei oder erstellen Sie eine Datei, die Sie sperren, wenn die Datei gesperrt ist und löschen Sie es, wenn es nicht mehr gesperrt ist (und dann sicherstellen Ihre Programme gehorchen diesen Semantiken).

+0

Der Code hier war sehr hilfreich. Sowie die Links! –

+0

Gibt es einen Grund, warum Sie append '>>' verwenden, anstatt '' 'zu erstellen/zu überschreiben? – xagyg

+0

Hinweis: Laut [der Dokumentation] (http://perldoc.perl.org/functions/flock.html) "Um die Möglichkeit einer Fehlkoordinierung zu vermeiden, spült Perl $ fh, bevor es gesperrt oder entsperrt wird." Spülen beseitigen die Möglichkeit eines Race Condition? –

0

Verwenden Sie die flock Luke.

Bearbeiten:This ist eine gute Erklärung.

+0

Die Links sind toll –

0

flock erstellt Unix-ähnliche Dateisperren und ist auf den meisten Betriebssystemen verfügbar, auf denen Perl läuft. Locks sind jedoch nur beratend.

edit: betont, dass Herde

+0

Während dies hilfreich ist zurück Informationen es nicht wirklich Beantworten Sie die Frage –

7

CPAN zur Rettung: IO::LockedFile.

+0

Ahhh !!! Objektorientierter Perl-Effekt. Die IO :: LockedFile ist mit den Flock-Funktionen implementiert –

+0

Das CPAN-Modul sieht gut aus.Ich würde lieber Code wiederverwenden als jedes Mal selbst, selbst wenn es nur ein paar Zeilen sind. Perl OO Syntax ist ziemlich abscheulich, aber es funktioniert in Ordnung. –

1

Mein Ziel in dieser Frage war, eine Datei zu sperren, die als Datenspeicher für mehrere Skripts verwendet wird. Am Ende habe ich einen ähnlichen Code der folgenden (von Chris):

open (FILE, '>>', test.dat') ; # open the file 
flock FILE, 2; # try to lock the file 
# do something with the file here 
close(FILE); # close the file 

In seinem Beispiel entfernte ich die Herde FILE, 8 als close (FILE) führt diese Aktion auch. Das eigentliche Problem war, wenn das Skript gestartet wird, muss es den aktuellen Zähler halten, und wenn es endet, muss es den Zähler aktualisieren. Dies ist, wo Perl ein Problem hat, um die Datei zu lesen:

open (FILE, '<', test.dat'); 
flock FILE, 2; 

Jetzt möchte ich die Ergebnisse schreiben, und da will ich die Datei ich wieder zu öffnen, müssen überschreiben und gestutzt, die in den folgenden Ergebnissen:

open (FILE, '>', test.dat'); #single arrow truncates double appends 
flock FILE, 2; 

In diesem Fall ist die Datei tatsächlich für eine kurze Zeit entsperrt, während die Datei erneut geöffnet wird. Dies demonstriert den Fall für die externe Sperrdatei. Wenn Sie die Kontexte der Datei ändern möchten, verwenden Sie eine Sperrdatei. Der modifizierte Code:

open (LOCK_FILE, '<', test.dat.lock') or die "Could not obtain lock"; 
flock LOCK_FILE, 2; 
open (FILE, '<', test.dat') or die "Could not open file"; 
# read file 
# ... 
open (FILE, '>', test.dat') or die "Could not reopen file"; 
#write file 
close (FILE); 
close (LOCK_FILE); 
4

Haben Sie darüber nachgedacht, die LockFile::Simple module mit? Es macht den größten Teil der Arbeit für Sie bereits.

Nach meiner Erfahrung habe ich festgestellt, dass es sehr einfach zu bedienen und robust ist.

+0

Das sieht etwas komplizierter aus als die Flockstruktur. Ich weiß nichts über Nebenläufigkeitsprobleme von Herden, aber hier werden speziell die Race-Condition-Möglichkeiten von NFS erwähnt, die wir verwenden werden. –

5

Ich denke, es wäre viel besser, dies mit lexikalischen Variablen als Dateihandler und Fehlerbehandlung zu zeigen. Es ist auch besser, die Konstanten aus dem Fcntl-Modul zu verwenden, als die magische Zahl 2 hart zu codieren, die unter allen Betriebssystemen nicht die richtige Zahl ist.

 
    use Fcntl ':flock'; # import LOCK_* constants 

    # open the file for appending 
    open (my $fh, '>>', 'test.dat') or die $!; 

    # try to lock the file exclusively, will wait till you get the lock 
    flock($fh, LOCK_EX); 

    # do something with the file here (print to it in our case) 

    # actually you should not unlock the file 
    # close the file will unlock it 
    close($fh) or warn "Could not close file $!"; 

Schauen Sie sich die volle documentation of flock und die File locking tutorial auf PerlMonks obwohl das auch den alten Stil der Datei-Handle Verwendung verwendet.

Eigentlich überspringe ich normalerweise die Fehlerbehandlung bei close(), da es nicht ist, kann ich viel tun, wenn es trotzdem fehlschlägt.

Wenn Sie in einer Datei arbeiten, sperren Sie diese Datei. Wenn Sie mehrere Dateien gleichzeitig sperren müssen, ist es besser, eine Datei auszuwählen, die Sie sperren, um Deadlocks zu vermeiden. Es spielt keine Rolle, ob das eine der mehreren Dateien ist, die Sie wirklich sperren müssen, oder eine separate Datei, die Sie nur zum Sperren erstellen.

+2

Sie sollten 'close' trotzdem überprüfen. Sie können nicht viel tun, aber Sie können dem Benutzer wenigstens sagen und aussteigen, anstatt still zu rennen, als ob nichts passiert wäre. –

+0

sollten Sie vor allem überprüfen, ob Flock ausgefallen ist. Sicher, die meiste Zeit wird es nicht, aber wenn es passiert, würden Sie in großen Schwierigkeiten stecken. Je nach Perlflock-Implementierung siehe [flock (2)] (http://linux.die.net/man/2/flock), [lockf (3)] (http://linux.die.net/man/3/lockf) oder [fcntl (2)] (http://linux.die.net/man/2/fcntl) –

6

Ryan P schrieb:

In diesem Fall wird die Datei tatsächlich für kurze Zeit freigeschaltet wird, während die Datei geöffnet wird.

Also tun Sie das nicht. Stattdessen open die Datei zum Lesen/Schreiben:

open my $fh, '+<', 'test.dat' 
    or die "Couldn’t open test.dat: $!\n"; 

Wenn Sie bereit sind, die Zähler, nur seek zurück zum Anfang der Datei zu schreiben. Wenn Sie das tun, sollten Sie truncate kurz vor close, so dass die Datei nicht mit abschließendem Müll zurückgelassen wird, wenn seine neuen Inhalte kürzer als seine vorherigen sind. (In der Regel ist die aktuelle Position in der Datei am Ende, so können Sie einfach truncate $fh, tell $fh schreiben.)

Beachten Sie auch, dass ich drei-Argument open und ein lexikalisches Datei-Handle verwendet, und ich habe auch den Erfolg der Betrieb. Bitte vermeiden Sie globale Datei-Handles (globale Variablen sind schlecht, mmkay?) Und magische Zwei-Argument open (die eine Quelle vieler (n ausnutzbare) Bug in Perl-Code war), und immer testen, ob Ihre open s erfolgreich sind.

+0

Überspringt die Suche an den Anfang tatsächlich den Inhalt der Datei? Das war mein spezifisches Problem. Ich teste mit dem Würfel, ich habe nur versucht, den Code im Beispiel einfach zu halten. Ich dachte nie an die Exploit der 2 Arg offen, danke, dass ich das im Hinterkopf behalten werde. –

+1

Ja, es überschreibt den Inhalt. Aber ich habe vergessen zu erwähnen, dass Sie die Datei abschneiden müssen, bevor Sie sie schließen, um sicherzustellen, dass die Datei, wenn der neue Inhalt kürzer ist, nicht mit einem Abfall endet. –

3
use strict; 

use Fcntl ':flock'; # Import LOCK_* constants 

# We will use this file path in error messages and function calls. 
# Don't type it out more than once in your code. Use a variable. 
my $file = '/path/to/some/file'; 

# Open the file for appending. Note the file path is in quoted 
# in the error message. This helps debug situations where you 
# have a stray space at the start or end of the path. 
open(my $fh, '>>', $file) or die "Could not open '$file' - $!"; 

# Get exclusive lock (will block until it does) 
flock($fh, LOCK_EX); 


# Do something with the file here... 


# Do NOT use flock() to unlock the file if you wrote to the 
# file in the "do something" section above. This could create 
# a race condition. The close() call below will unlock it 
# for you, but only after writing any buffered data. 

# In a world of buffered i/o, some or all of your data will not 
# be written until close() completes. Always, always, ALWAYS 
# check the return value on close()! 
close($fh) or die "Could not write '$file' - $!"; 
0

Hier ist meine Lösung zu Lesen und Schreiben in ein Schloss ...

open (TST,"+< readwrite_test.txt") or die "Cannot open file\n$!"; 
flock(TST, LOCK_EX); 
# Read the file: 
@LINES=<TST>; 
# Wipe the file: 
seek(TST, 0, 0); truncate(TST, 0); 
# Do something with the contents here: 
push @LINES,"grappig, he!\n"; 
$LINES[3]="Gekke henkie!\n"; 
# Write the file: 
foreach $l (@LINES) 
{ 
    print TST $l; 
} 
close(TST) or die "Cannot close file\n$!"; 
11

Die anderen Antworten Perl Herde ziemlich gut verriegeln, aber auf vielen Unix/Linux-Systemen gibt es tatsächlich zwei unabhängige Schließanlagen Systeme: BSD flock() und POSIX fcntl() - basierte Schlösser.

Sofern Sie beim Erstellen von Perl keine speziellen Konfigurationsoptionen angeben, verwendet der Flock flock(), sofern verfügbar. Dies ist im Allgemeinen gut und wahrscheinlich, was Sie wollen, wenn Sie nur innerhalb Ihrer Anwendung (auf einem einzigen System ausgeführt) sperren müssen. Manchmal müssen Sie jedoch mit einer anderen Anwendung interagieren, die fcntl() -Sperren verwendet (wie Sendmail auf vielen Systemen), oder Sie müssen Dateisperren über NFS-gemountete Dateisysteme durchführen.

In diesen Fällen können Sie sich File::FcntlLock oder File::lockf ansehen. Es ist auch möglich, fcntl() - basiertes Sperren in reinem Perl (mit einigen behaarten und nicht portablen Bits von pack()) durchzuführen.

Schneller Überblick über die Herde/Fcntl/lockf Unterschiede:

lockf fast immer an der Spitze Fcntl implementiert ist, hat auf Dateiebene Verriegelung nur. Wenn sie mit fcntl implementiert werden, gelten die folgenden Einschränkungen auch für lockf.

fcntl bietet Sperren auf Bereichsebene (innerhalb einer Datei) und Netzwerksperren über NFS, Sperren werden jedoch nicht von untergeordneten Prozessen nach einem fork() geerbt. Auf vielen Systemen müssen Sie das Dateihandle schreibgeschützt öffnen, um eine gemeinsame Sperre anzufordern, und lesen, um eine exklusive Sperre anzufordern.

flock ist nur auf Dateiebene sperrbar, Sperren ist nur innerhalb einer einzelnen Maschine möglich (Sie können eine NFS-gemountete Datei sperren, aber nur lokale Prozesse sehen die Sperre). Sperren werden von Kindern geerbt (vorausgesetzt, dass der Dateideskriptor nicht geschlossen ist).

Manchmal (SYSV-Systeme) Flock wird mit Lockf oder fcntl emuliert; Auf einigen BSD-Systemen wird Lockf mit Flock emuliert. Im Allgemeinen funktionieren diese Emulationsarten schlecht und Sie sind gut beraten, sie zu vermeiden.

+2

Danke für die schöne Erklärung der Unterschiede zwischen den zugrundeliegenden Systemaufrufen. Die Wikipedia [Liste der Unix-Dateisperrprobleme] (http://en.wikipedia.org/wiki/File_locking#Problems) sollte wahrscheinlich gelesen werden müssen. – tchrist

1

aus der entwickelten http://metacpan.org/pod/File::FcntlLock

use Fcntl qw(:DEFAULT :flock :seek :Fcompat); 
use File::FcntlLock; 
sub acquire_lock { 
    my $fn = shift; 
    my $justPrint = shift || 0; 
    confess "Too many args" if defined shift; 
    confess "Not enough args" if !defined $justPrint; 

    my $rv = TRUE; 
    my $fh; 
    sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!"; 
    $fh->autoflush(1); 
    ALWAYS "acquiring lock: $fn"; 
    my $fs = new File::FcntlLock; 
    $fs->l_type(F_WRLCK); 
    $fs->l_whence(SEEK_SET); 
    $fs->l_start(0); 
    $fs->lock($fh, F_SETLKW) or LOGDIE "failed to get write lock: $fn:" . $fs->error; 
    my $num = <$fh> || 0; 
    return ($fh, $num); 
} 

sub release_lock { 
    my $fn = shift; 
    my $fh = shift; 
    my $num = shift; 
    my $justPrint = shift || 0; 

    seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!"; 
    print $fh "$num\n" or LOGDIE "write failed: $fn: $!"; 
    truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!"; 
    my $fs = new File::FcntlLock; 
    $fs->l_type(F_UNLCK); 
    ALWAYS "releasing lock: $fn"; 
    $fs->lock($fh, F_SETLK) or LOGDIE "unlock failed: $fn: " . $fs->error; 
    close($fh) or LOGDIE "close failed: $fn: $!"; 
} 
1

Eine Alternative zum Schloss Datei Ansatz eine Sperre Buchse zu verwenden ist. Für eine solche Implementierung siehe Lock::Socket auf CPAN. Die Benutzung ist so einfach wie die folgende:

use Lock::Socket qw/lock_socket/; 
my $lock = lock_socket(5197); # raises exception if lock already taken 

Es gibt ein paar Vorteile bei der Verwendung einer Buchse:

  • garantiert (über das Betriebssystem), dass keine zwei Anwendungen die gleiche Sperre halten: es ist keine Wettlaufbedingung.
  • garantiert (wieder durch das Betriebssystem) sauber säubern, wenn Ihr Prozess beendet, so dass es keine abgestandenen Sperren zu tun haben.
  • beruht auf Funktionalität, die von allem gut unterstützt wird, auf dem Perl läuft: keine Probleme mit der Unterstützung von Flock (2) auf Win32 zum Beispiel.

Der offensichtliche Nachteil ist natürlich, dass der Sperrnamespace global ist. Es kann zu einer Art Denial-of-Service kommen, wenn ein anderer Prozess entscheidet, den benötigten Port zu sperren.

[Offenlegung: Ich bin der Autor des AFOR genannten Modul]

+0

sollte es "verwenden Lock :: Socket", nicht "verwenden Lock :: Simple" –

+0

Dank Matija Nalis! Ich habe das Beispiel behoben. –

Verwandte Themen