Cerrojos sobre Ficheros

El siguiente código muestra como usar la función flock para sincronizar los accesos a un fichero utilizado para la comunicación entre los mismos.

La Función flock

La sintáxis de flock es:

$boolean = flock(FILEHANDLE, $how)
El primer argumento es el manejador de ficheros y el segundo una constante numérica que indica el tipo de acceso. Toma uno de los valores LOCK_SH, LOCK_EX o LOCK_UN los cuáles estan definidos en el módulo Fcntl .

La función flock utiliza una política con bloqueo y advisory locking. Los cerrojos se liberan cuando se cierra el fichero. Además el cierre provoca el vaciado de los buffers. Algunas versiones de flock no permiten bloquear en un sistema de archivos en red; para ello hay que usar la función (dependiente del sistema) fcntl .

Ejecución

En el ejemplo se activan un número de procesos con identificadores lógicos entre 0 y $np que acceden concurrentemente a un fichero sync.txt añadiendo cada uno de los procesos su identificador al resultado previamente almacenado en el fichero:

lhp@nereida:~/Lperl/src/perl_networking/ch2$ ./flock.pl  6
Process 2 reads 0.
Process 2 writing 2.
Process 3 reads 2.
Process 3 writing 5.
Process 4 reads 5.
Process 4 writing 9.
Process 6 reads 9.
Process 6 writing 15.
Process 5 reads 15.
Process 5 writing 20.
Process 1 reads 20.
Process 1 writing 21.

Código del Ejemplo

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n flock.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use Fcntl qw(:DEFAULT :flock);
 4  use POSIX qw(WNOHANG);
 5  $| = 1;
 6
 7  local $SIG{CHLD} = sub {
 8    while (my $kid = waitpid(-1, WNOHANG) > 0) {}
 9  };
10
11  sub create_child {
12    my ($id, $task) = splice @_, 0, 2;
13    my $pid;
14
15    return $pid if $pid = fork();
16    die "Cannot fork $!" unless defined $pid;
17    $task->($id, @_); # do something
18    exit;
19  }
20
21  sub parfor {
22    my $LAST = shift;
23    my $task = shift;
24    my @pid;
25
26    $pid[0] = $$;
27    $pid[$_] = create_child($_, $task, @_) for 1..$LAST;
28    return @pid;
29  }
30
31  sub task {
32    my $id = shift;
33    my $fn = shift;
34
35    sleep(int(rand(2)));
36    open my $f, "+<$fn" or die "Can't open $fn. $!\n";
37    flock($f, 2);
38    seek $f, 0, 0;
39    my $num = <$f>;
40    warn "Process $id reads $num.\n";
41    seek $f, 0, 0;
42    my $s = $num+$id;
43    warn "Process $id writing $s.\n";
44    print $f $s;
45    close($f);
46    exit;
47  }
48
49  #main
50  my $np = shift || 3;
51  my $fn = shift || "sync.txt";
52  open my $f, "> $fn" or die "Can't open file $fn. $!\n";
53  print $f 0;
54  close($f);
55  &parfor($np, \&task, $fn);
56  do {} while wait > 0;

La función sysopen

La función sysopen tiene el formato:

sysopen FILEHANDLE,FILENAME,MODE,PERMS

Es posible establecer modos de acceso a un nivel mas detallado. Para ello se especifica el modo haciendo un o lógico de constantes definidas en Fcntl . Por ejemplo:

sysopen (HTML, 'myhtml.html', O_RDWR|O_EXCL|O_CREAT, 0755);

Algunos valores que pueden ser usados son:
Valor Definición
O_RDWR Lectura y Escritura
O_RDONLY Sólo Lectura
O_WRONLY Sólo Escritura
O_CREAT Crear el Fichero
O_APPEND Añadir
O_TRUNC Truncar
O_EXCL Parar si el fichero existe
O_NONBLOCK Sin bloqueo

Ejercicio 3.5.1   Lea la columna Cleaning Up Log files http://www.stonehenge.com/merlyn/UnixReview/col23.html de Randall Schwartz para ams ejemplos de como usar flock.



Subsecciones
Casiano Rodríguez León
2012-02-29