Question

Quelle est la meilleure façon de créer un verrou sur un fichier en Perl ?

Est-il préférable de se regrouper sur le fichier ou de créer un fichier de verrouillage pour placer un verrou et vérifier s'il y a un verrou sur le fichier de verrouillage ?

Était-ce utile?

La solution

Si vous finissez par utiliser flock, voici un code pour le faire :

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

Quelques liens utiles :

En réponse à votre question ajoutée, je dirais soit de placer le verrou sur le fichier, soit de créer un fichier que vous appelez « verrouiller » chaque fois que le fichier est verrouillé et de le supprimer lorsqu'il n'est plus verrouillé (puis de vous assurer que vos programmes obéissent). cette sémantique).

Autres conseils

Les autres réponses couvrent assez bien le verrouillage floqué Perl, mais sur de nombreux systèmes Unix/Linux, il existe en fait deux systèmes de verrouillage indépendants :Verrous basés sur BSD flock() et POSIX fcntl().

À moins que vous ne fournissiez des options spéciales à configurer lors de la construction de Perl, son flock utilisera flock() si disponible.C'est généralement bien et c'est probablement ce que vous souhaitez si vous avez simplement besoin d'un verrouillage au sein de votre application (exécutée sur un seul système).Cependant, vous devez parfois interagir avec une autre application qui utilise les verrous fcntl() (comme Sendmail, sur de nombreux systèmes) ou peut-être devez-vous effectuer un verrouillage de fichiers sur des systèmes de fichiers montés sur NFS.

Dans ces cas-là, vous voudrez peut-être examiner Fichier ::FcntlLock ou Fichier ::lockf.Il est également possible d'effectuer un verrouillage basé sur fcntl() en Perl pur (avec quelques morceaux poilus et non portables de pack()).

Aperçu rapide des différences flock/fcntl/lockf :

lockf est presque toujours implémenté au-dessus de fcntl et n'a qu'un verrouillage au niveau du fichier.Si elles sont implémentées à l'aide de fcntl, les limitations ci-dessous s'appliquent également à lockf.

fcntl fournit un verrouillage au niveau de la plage (au sein d'un fichier) et un verrouillage réseau sur NFS, mais les verrous ne sont pas hérités par les processus enfants après un fork().Sur de nombreux systèmes, le descripteur de fichier doit être ouvert en lecture seule pour demander un verrou partagé, et en lecture-écriture pour demander un verrou exclusif.

flock a un verrouillage au niveau du fichier uniquement, le verrouillage s'effectue uniquement sur une seule machine (vous pouvez verrouiller un fichier monté par NFS, mais seuls les processus locaux verront le verrou).Les verrous sont hérités par les enfants (en supposant que le descripteur de fichier n'est pas fermé).

Parfois (systèmes SYSV), le flock est émulé à l'aide de lockf ou fcntl ;sur certains systèmes BSD, lockf est émulé en utilisant flock.Généralement, ces types d'émulation fonctionnent mal et il est conseillé de les éviter.

Le CPAN à la rescousse : IO ::Fichier verrouillé.

Ryan P a écrit :

Dans ce cas, le fichier est effectivement déverrouillé pendant une courte période le temps de sa réouverture.

Alors ne fais pas ça.Plutôt, open le fichier en lecture/écriture :

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

Lorsque vous êtes prêt à écrire le compteur, il suffit seek retour au début du fichier.Notez que si vous faites cela, vous devriez truncate juste avant close, afin que le fichier ne se retrouve pas avec des déchets de fin si son nouveau contenu est plus court que le précédent.(Habituellement, la position actuelle dans le fichier est à sa fin, vous pouvez donc simplement écrire truncate $fh, tell $fh.)

Notez également que j'ai utilisé trois arguments open et un descripteur de fichier lexical, et j'ai également vérifié le succès de l'opération.Veuillez éviter les descripteurs de fichiers globaux (les variables globales sont mauvaises, mmkay ?) et les deux arguments magiques open (qui a été la source de nombreux bugs (exploitables) dans le code Perl), et testez toujours si votre opens réussir.

Je pense qu'il serait beaucoup mieux de le montrer avec des variables lexicales en tant que gestionnaires de fichiers et gestion des erreurs.Il est également préférable d'utiliser les constantes du module Fcntl plutôt que de coder en dur le nombre magique 2 qui n'est peut-être pas le bon nombre sur tous les systèmes d'exploitation.

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

Découvrez l'intégralité documentation du troupeau et le Tutoriel sur le verrouillage de fichiers sur PerlMonks même si cela utilise également l'ancien style d'utilisation des descripteurs de fichiers.

En fait, je saute généralement la gestion des erreurs sur close () car je ne peux pas faire grand-chose si elle échoue de toute façon.

Concernant ce qu'il faut verrouiller, si vous travaillez sur un seul fichier, verrouillez ce fichier.Si vous devez verrouiller plusieurs fichiers à la fois, afin d'éviter les blocages morts, il est préférable de choisir un seul fichier que vous verrouillez.Cela n'a pas vraiment d'importance s'il s'agit de l'un des nombreux fichiers que vous devez réellement verrouiller ou d'un fichier séparé que vous créez uniquement à des fins de verrouillage.

Avez-vous envisagé d'utiliser le LockFile ::Module simple?Il fait déjà la majeure partie du travail à votre place.

D’après mon expérience passée, je l’ai trouvé très facile à utiliser et robuste.

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

Mon objectif dans cette question était de verrouiller un fichier utilisé comme magasin de données pour plusieurs scripts.En fin de compte, j'ai utilisé un code similaire au suivant (de 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

Dans son exemple, j'ai supprimé le flock FILE, 8 car le close(FILE) effectue également cette action.Le vrai problème était que lorsque le script démarre, il doit conserver le compteur actuel et qu'à la fin, il doit mettre à jour le compteur.C'est là que Perl a un problème, pour lire le fichier :

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

Maintenant, je veux écrire les résultats et comme je veux écraser le fichier, je dois le rouvrir et le tronquer, ce qui donne ce qui suit :

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

Dans ce cas, le fichier est effectivement déverrouillé pendant une courte période le temps de sa réouverture.Ceci illustre le cas du fichier de verrouillage externe.Si vous envisagez de modifier les contextes du fichier, utilisez un fichier de verrouillage.Le code modifié :

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);

Développé à partir de 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: $!";
}

Une alternative à la serrure déposer l'approche consiste à utiliser un verrou prise.Voir Verrouillage : Socket sur CPAN pour une telle implémentation.L'utilisation est aussi simple que ce qui suit :

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

L'utilisation d'un socket présente plusieurs avantages :

  • garanti (via le système d'exploitation) qu'aucune application ne détiendra le même verrou :il n'y a pas de condition de concurrence.
  • garanti (encore une fois via le système d'exploitation) un nettoyage soigné à la fin de votre processus, il n'y a donc pas de verrous obsolètes à gérer.
  • s'appuie sur des fonctionnalités bien prises en charge par tout ce sur quoi Perl s'exécute :aucun problème avec le support de flock(2) sur Win32 par exemple.

L’inconvénient évident est bien sûr que l’espace de noms du verrou est global.Une sorte de déni de service est possible si un autre processus décide de verrouiller le port dont vous avez besoin.

[divulgation:Je suis l'auteur du module mentionné ci-dessus]

Utilisez le troupeau Luc.

Modifier: Ce est une bonne explication.

flock crée des verrous de fichiers de style Unix et est disponible sur la plupart des systèmes d'exploitation sur lesquels Perl s'exécute.Cependant, les serrures du troupeau sont uniquement à titre indicatif.

modifier:a souligné que le troupeau est portable

Voici ma solution pour lire et écrire dans un seul verrou...

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$!";
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top