Frage

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

Ist es am besten, die Datei zu überprüfen oder eine Sperrdatei zu erstellen, um sie zu sperren und zu prüfen, ob die Sperrdatei gesperrt ist?

War es hilfreich?

Lösung

Wenn Sie am Ende Flock verwenden, finden Sie hier einen Code dafür:

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 hinzugefügte Frage würde ich sagen, dass Sie entweder die Datei sperren oder eine Datei erstellen, die Sie „Sperren“ nennen, wenn die Datei gesperrt ist, und sie löschen, wenn sie nicht mehr gesperrt ist (und dann sicherstellen, dass Ihre Programme gehorchen). diese Semantik).

Andere Tipps

Die anderen Antworten decken Perl-Flocksperren ziemlich gut ab, aber auf vielen Unix/Linux-Systemen gibt es tatsächlich zwei unabhängige Sperrsysteme:Auf BSD flock() und POSIX fcntl() basierende Sperren.

Sofern Sie beim Erstellen von Perl keine speziellen Konfigurationsoptionen bereitstellen, verwendet der Flock flock(), sofern verfügbar.Dies ist im Allgemeinen in Ordnung und wahrscheinlich das, was Sie wollen, wenn Sie nur eine Sperre innerhalb Ihrer Anwendung benötigen (die auf einem einzelnen System ausgeführt wird).Manchmal müssen Sie jedoch mit einer anderen Anwendung interagieren, die fcntl()-Sperren verwendet (wie Sendmail auf vielen Systemen), oder Sie müssen möglicherweise Dateisperren über NFS-gemountete Dateisysteme hinweg durchführen.

In diesen Fällen sollten Sie einen Blick darauf werfen Datei::FcntlLock oder Datei::lockf.Es ist auch möglich, fcntl()-basiertes Sperren in reinem Perl durchzuführen (mit einigen umständlichen und nicht tragbaren Teilen von pack()).

Kurzer Überblick über die Unterschiede zwischen flock/fcntl/lockf:

lockf wird fast immer zusätzlich zu fcntl implementiert und verfügt nur über eine Sperrung auf Dateiebene.Bei Implementierung mit fcntl 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 muss das Dateihandle schreibgeschützt geöffnet sein, um eine gemeinsame Sperre anzufordern, und mit Lese-/Schreibzugriff, um eine exklusive Sperre anzufordern.

flock verfügt nur über eine Sperrung auf Dateiebene, die Sperrung erfolgt nur innerhalb eines einzelnen Computers (Sie können eine über NFS bereitgestellte Datei sperren, aber nur lokale Prozesse sehen die Sperre).Sperren werden von Kindern geerbt (vorausgesetzt, der Dateideskriptor ist nicht geschlossen).

Manchmal (SYSV-Systeme) wird flock mit lockf oder fcntl emuliert;Auf einigen BSD-Systemen wird lockf mit flock emuliert.Im Allgemeinen funktionieren diese Arten der Emulation schlecht und Sie sind gut beraten, sie zu vermeiden.

CPAN zur Rettung: IO::LockedFile.

Ryan P schrieb:

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

Also tu 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, den Zähler zu schreiben, einfach seek zurück zum Anfang der Datei.Beachten Sie, dass Sie dies tun sollten, wenn Sie dies tun truncate kurz bevor close, damit die Datei nicht mit nachgestelltem Müll zurückbleibt, wenn ihr neuer Inhalt kürzer als der vorherige ist.(Normalerweise befindet sich die aktuelle Position in der Datei am Ende, sodass Sie einfach schreiben können truncate $fh, tell $fh.)

Beachten Sie außerdem, dass ich drei Argumente verwendet habe open und ein lexikalisches Dateihandle, und ich habe auch den Erfolg der Operation überprüft.Bitte vermeiden Sie globale Dateihandles (globale Variablen sind schlecht, nicht wahr?) und magische Zwei-Argumente open (was eine Quelle vieler (ausnutzbarer) Fehler im Perl-Code war) und testen Sie immer, ob Ihre openEs gelingt uns.

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 fest zu codieren, die möglicherweise nicht auf allen Betriebssystemen 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 das Ganze an Dokumentation der Herde und das Tutorial zum Sperren von Dateien auf PerlMonks, obwohl auch die alte Art der Dateihandle-Nutzung verwendet wird.

Tatsächlich überspringe ich normalerweise den Fehlerhandling bei Close (), da ich nicht viel tun kann, wenn er sowieso fehlschlägt.

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

Haben Sie darüber nachgedacht, das zu verwenden? LockFile::Simple-Modul?Den Großteil der Arbeit erledigt es bereits für Sie.

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

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' - $!";

Mein Ziel bei dieser Frage war es, eine Datei zu sperren, die als Datenspeicher für mehrere Skripte verwendet wird.Am Ende habe ich einen ähnlichen Code wie den folgenden verwendet (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 habe ich die Herde FILE, 8 entfernt, da close(FILE) diese Aktion ebenfalls ausführt.Das eigentliche Problem bestand darin, dass das Skript beim Start den aktuellen Zähler halten und beim Beenden den Zähler aktualisieren muss.Hier hat Perl ein Problem. Um die Datei zu lesen, gehen Sie wie folgt vor:

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

Jetzt möchte ich die Ergebnisse ausschreiben und da ich die Datei überschreiben möchte, muss ich sie erneut öffnen und abschneiden, was zu Folgendem führt:

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

In diesem Fall wird die Datei tatsächlich für kurze Zeit entsperrt, während die Datei erneut geöffnet wird.Dies zeigt den Fall der externen Sperrdatei.Wenn Sie den Kontext der Datei ändern möchten, verwenden Sie eine Sperrdatei.Der geänderte 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);

Entwickelt aus 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: $!";
}

Eine Alternative zum Schloss Datei Der Ansatz besteht darin, ein Schloss zu verwenden Steckdose.Sehen Sperre::Socket auf CPAN für eine solche Implementierung.Die Verwendung ist so einfach wie folgt:

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

Die Verwendung eines Sockets bietet einige Vorteile:

  • garantiert (durch das Betriebssystem), dass keine zwei Anwendungen die gleiche Sperre haben:Es gibt keine Rennbedingung.
  • garantiert (wiederum durch das Betriebssystem), dass er sauber bereinigt wird, wenn Ihr Prozess beendet wird, so dass es keine veralteten Sperren gibt, mit denen man sich befassen muss.
  • setzt auf eine Funktionalität, die von allem, auf dem Perl läuft, gut unterstützt wird:Keine Probleme mit der flock(2)-Unterstützung beispielsweise unter Win32.

Der offensichtliche Nachteil ist natürlich, dass der Lock-Namespace global ist.Es kann zu einer Art Denial-of-Service kommen, wenn ein anderer Prozess beschließt, den von Ihnen benötigten Port zu sperren.

[Offenlegung:Ich bin der Autor des oben genannten Moduls]

Benutzen Sie die Herde Lukas.

Bearbeiten: Das ist eine gute Erklärung.

flock erstellt Dateisperren im Unix-Stil und ist auf den meisten Betriebssystemen verfügbar, auf denen Perl ausgeführt wird.Allerdings dienen Flock-Sperren nur als Empfehlung.

bearbeiten:betonte, dass Flock tragbar sei

Hier ist meine Lösung zum Lesen und Schreiben in einem 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$!";
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top