2008-08-29 13 views
25

¿Cuál es la mejor manera de crear un bloqueo en un archivo en Perl?¿Cómo bloqueo un archivo en Perl?

¿Es mejor agruparse en el archivo o crear un archivo de bloqueo para colocar un candado y comprobar si hay un candado en el archivo de bloqueo?

+1

nota sobre cómo escribir Perl: http://stackoverflow.com/questions/72312/how-should-i-capitalize- perl – szabgab

+0

no estoy seguro de si los chicos perl de aquí son lily para hacer un 'faq' como C++, pero yo diría que este es un buen ejemplo de uno – thecoshman

Respuesta

27

Si terminas usando rebaño, aquí hay un código para hacerlo:

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

Algunos enlaces útiles:

En respuesta a su pregunta adicional, diría que coloque el candado en el archivo o cree un archivo que llame 'bloquear' cada vez que el archivo esté bloqueado y elimínelo cuando ya no esté bloqueado (y luego asegúrese tus programas obedecen esa semántica).

+0

El código aquí fue de gran ayuda. ¡Además de los enlaces! –

+0

¿Hay alguna razón por la que use append '>>' en lugar de crear/sobrescribir '>'? – xagyg

+0

Nota: Según [la documentación] (http://perldoc.perl.org/functions/flock.html) '" Para evitar la posibilidad de falta de coordinación, Perl ahora vacía $ fh antes de bloquearlo o desbloquearlo ". lavado elimina la posibilidad de una condición de carrera? –

0

Utilice flock Luke.

Editar:This es una buena explicación.

+0

Los enlaces son geniales –

0

flock crea bloqueos de archivos de estilo Unix, y está disponible en la mayoría de las ejecuciones de Perl del sistema operativo. Sin embargo, los bloqueos de bandadas son solo de asesoramiento.

edición: hizo hincapié en que rebaño es portátil

+0

Si bien esta información es útil, no es realmente responde la pregunta –

7

CPAN al rescate: IO::LockedFile.

+0

Ahhh !!! Objeto orientado Perl eek. IO :: LockedFile se implementa utilizando las funciones de grupo –

+0

El módulo CPAN me parece bien.Prefiero reutilizar el código que deletrearlo cada vez, incluso si solo son un par de líneas. La sintaxis de Perl OO es bastante horrible, pero funciona bien. –

1

Mi objetivo en esta pregunta era bloquear un archivo que se utiliza como un almacén de datos para varias secuencias de comandos. Al final utilicé código similar al siguiente (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 

En su ejemplo que quita el archivo rebaño, 8 como la estrecha (FILE) lleva a cabo esta acción también. El verdadero problema fue cuando el script comienza tiene que contener el contador actual, y cuando termina tiene que actualizar el contador. Aquí es donde Perl tiene un problema, para leer el archivo que:

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

Ahora quiero escribir los resultados y que la quiero para sobrescribir el archivo que necesito para volver a abrir y truncar lo que se traduce en lo siguiente:

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

En este caso, el archivo está realmente desbloqueado durante un corto período de tiempo mientras se vuelve a abrir el archivo. Esto demuestra el caso del archivo de bloqueo externo. Si va a cambiar los contextos del archivo, use un archivo de bloqueo. El código modificado:

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

¿Ha considerado utilizar el LockFile::Simple module? Ya hace la mayor parte del trabajo por ti.

En mi experiencia pasada, he encontrado que es muy fácil de usar y resistente.

+0

Esto parece un poco más complicado que la estructura de la bandada. No conozco los problemas de simultaneidad de las bandadas, pero esto específicamente señala las posibilidades de condición de carrera de NFS que vamos a utilizar. –

5

Creo que sería mucho mejor mostrar esto con variables léxicas como manejadores de archivos y manejo de errores. También es mejor usar las constantes del módulo Fcntl que codificar el número mágico 2 que podría no ser el número correcto en todos los sistemas operativos.

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

Mira la plena documentation of flock y la File locking tutorial en PerlMonks a pesar de que también utiliza el viejo estilo de uso de identificador de archivo.

En realidad, generalmente omito el manejo de errores al cerrar() ya que no hay que pueda hacer si falla de todos modos.

En cuanto a qué bloquear, si está trabajando en un solo archivo, bloquee ese archivo. Si necesita bloquear varios archivos a la vez, entonces - para evitar bloqueos muertos - es mejor elegir un archivo que está bloqueando. Realmente no importa si ese es uno de los varios archivos que realmente necesita bloquear o un archivo separado que crea solo para el propósito de bloqueo.

+2

Debe verificar 'cerrar' de todos modos. No puedes hacer mucho, pero al menos puedes decirle al usuario y rescatarlo, en lugar de seguir corriendo en silencio como si nada hubiera sucedido. –

+0

, debería verificar más si el lote falló. Claro, la mayoría de las veces no lo hará, pero cuando lo haga, estarás en un gran problema. Dependiendo de la implementación de la bandada perl ver [flock (2)] (http://linux.die.net/man/2/flock), [lockf (3)] (http://linux.die.net/man/3/lockf) o [fcntl (2)] (http://linux.die.net/man/2/fcntl) –

6

Ryan P escribió:

En este caso, el archivo está realmente desbloqueado por un corto período de tiempo, mientras que el archivo se vuelve a abrir.

Así que no hagas eso. En su lugar, open el archivo de lectura/escritura:

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

Cuando esté listo para escribir la receta, simplemente seek de nuevo al comienzo del archivo. Tenga en cuenta que si lo hace, debe truncate justo antes de close, de modo que el archivo no quede con basura arrastrada si sus nuevos contenidos son más cortos que los anteriores. (Por lo general, la posición actual en el archivo está al final, por lo que puede escribir truncate $fh, tell $fh.)

Además, tenga en cuenta que utilicé el argumento open y un manejador de archivo léxico, y también verifiqué el éxito del operación. Evite los identificadores de archivos globales (¿las variables globales son malas, mmkay?) Y el argumento mágico de dos argumentos open (que ha sido fuente de muchos errores (explotables) en el código Perl), y siempre pruebe si su open es correcto.

+0

¿La búsqueda de regreso al principio realmente sobrescribe el contenido del archivo? Ese fue mi problema específico. Estoy probando con el dado. Solo estaba tratando de mantener el código simple en el ejemplo. Nunca pensé en la explotación del 2 arg abierto gracias lo tendré en cuenta. –

+1

Sí, sobrescribirá el contenido. Pero olvidé mencionar que debe truncar el archivo antes de cerrarlo, para asegurarse de que si los nuevos contenidos son más cortos, el archivo no termine con basura arrastrada. –

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

aquí está mi solución a la lectura y la escritura en una cerradura ...

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

Las otras respuestas cubren Perl acuden bloqueo bastante bien, pero en muchos sistemas Unix/Linux en realidad hay dos de bloqueo independiente sistemas: BSD flock() y POSIX fcntl() basados ​​en bloqueos.

A menos que proporcione opciones especiales para configurar al construir Perl, su bandada usará bandada() si está disponible. En general, esto es bueno y probablemente lo que desee si solo necesita bloquear dentro de su aplicación (se ejecuta en un solo sistema). Sin embargo, algunas veces necesita interactuar con otra aplicación que use bloqueos fcntl() (como Sendmail, en muchos sistemas) o quizás necesite bloquear archivos en sistemas de archivos montados en NFS.

En esos casos, es posible que desee consultar File::FcntlLock o File::lockf. También es posible hacer un bloqueo basado en fcntl() en Perl puro (con algunos bits peludos y no portátiles del paquete()).

Breve resumen de las diferencias rebaño/fcntl/lockf:

lockf casi siempre se implementa sobre fcntl, tiene a nivel de archivo de bloqueo única. Si se implementa utilizando fcntl, las limitaciones a continuación también se aplican a lockf.

fcntl proporciona el bloqueo de nivel de rango (dentro de un archivo) y el bloqueo de red a través de NFS, pero los bloqueos no son heredados por los procesos secundarios después de una bifurcación(). En muchos sistemas, debe tener el manejador de archivos abierto de solo lectura para solicitar un bloqueo compartido y leer y escribir para solicitar un bloqueo exclusivo.

flock tiene bloqueo de nivel de archivo solamente, el bloqueo solo se realiza en una sola máquina (puede bloquear un archivo montado en NFS, pero solo los procesos locales verán el bloqueo). Los bloqueos son heredados por los niños (suponiendo que el descriptor de archivo no está cerrado).

A veces (sistemas SYSV) se emula el rebaño con lockf, o fcntl; en algunos sistemas BSD lockf se emula utilizando flock. En general, este tipo de emulación funciona mal y se recomienda evitarlas.

+2

Gracias por la excelente explicación de algunas de las distinciones entre las llamadas subyacentes del sistema. La [lista de problemas de bloqueo de archivos de Unix] de Wikipedia (http://en.wikipedia.org/wiki/File_locking#Problems) probablemente debería ser obligatoria. – tchrist

1

tecnica fuera 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: $!"; 
} 
1

Una alternativa al archivo de bloqueo enfoque es utilizar una toma de bloqueo . Ver Lock::Socket en CPAN para tal implementación. El uso es tan sencillo como lo siguiente:

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

Hay un par de ventajas para el uso de un enchufe:

  • garantizados (a través del sistema operativo) que no hay dos aplicaciones mantendrán la misma cerradura: no no es una condición de carrera.
  • garantizado (de nuevo a través del sistema operativo) para limpiar cuidadosamente cuando el proceso finaliza, por lo que no hay bloqueos añejos para hacer frente.
  • se basa en una funcionalidad que es bien soportada por cualquier cosa que ejecute Perl: sin problemas con el soporte de flock (2) en Win32, por ejemplo.

La desventaja obvia es, por supuesto, que el espacio de nombres de bloqueo es global. Es posible un tipo de denegación de servicio si otro proceso decide bloquear el puerto que necesita.

[divulgación: yo soy el autor del módulo de AFOR mencionado]

+0

debería ser "use Lock :: Socket", no "use Lock :: Simple" –

+0

Gracias Matija Nalis! He arreglado el ejemplo. –

Cuestiones relacionadas