Existe-t-il un moyen de créer un fichier verrouillé au point de création, dans Perl?
-
29-10-2019 - |
Question
J'ai besoin de créer un fichier verrouillé contre les lectures, au point de création, afin que d'autres processus qui peuvent aller chercher ce fichier ne commencent pas à le lire, avant qu'il ne soit complètement écrit.
Je sais que je peux créer et alors Verrouillez-le, mais je crains que cela me laisse ouvert à une condition de course.
Ou Est-ce que je m'inquiète de rien ici? Si j'ai un fichier ouvert pour l'écriture, puis l'ouvrez pour la lecture avec un autre processus, le processus de lecture ne verra-t-il jamais un EOF jusqu'à ce que le processus d'écriture ferme le fichier?
La solution
Il y a une condition de course avec >
et >>
, mais il peut être contourné en utilisant +<
.
# >
open(my $fh, '+<', $qfn) or die $!;
flock($fh, LOCK_EX) or die $!;
truncate($fh, 0) or die $!;
...
# >>
open(my $fh, '+<', $qfn) or die $!;
flock($fh, LOCK_EX) or die $!;
seek($fh, 0, SEEK_END) or die $!;
...
Il y a aussi une condition de course dans le scénario que vous décrivez.
Writer Reader
========================= =========================
- opens file
- opens file
- locks file
- obtains lock on file
- locks file [blocks] - reads the file [empty]
- closes and unlocks file
- obtains lock on file
- writes to file
- writes to file
- closes and unlocks file
Une stratégie courante pour éviter ce problème est d'avoir l'écrivain
- Créez le fichier dans un répertoire temporaire, puis
rename
Le fichier dans le répertoire du lecteur surveille lorsque le fichier est terminé.
rename
est une action atomique, donc le fichier semblera entièrement formé dans le répertoire que le lecteur surveille. Cela nécessite la coopération de l'écrivain, mais les meilleures solutions le feront.
Autres conseils
Utilisation umask(0777)
Avant de créer le fichier.
L'entrée du fichier dans le système de fichiers sera complètement inaccessible [*] (c'est-à-dire les autorisations ----------
), même si la poignée du fichier, vous avez toujours des permis.
Alors chmod()
le fichier une fois que vous avez terminé:
my $file = 'foo.txt';
my $umask = umask(0777); # change the umask
open(OUT, '>', $file); # create the file
umask($umask); # reset the umask
print OUT "testing\n"; # put stuff in your file
close(OUT); # finished with that...
chmod(0644, $file); # change the permissions
NB: Ce n'est pas réellement "verrouiller" dans le sens strict, où le système d'exploitation empêche activement l'accès aux fichiers. Il s'agit d'un "piratage" au niveau du système de fichiers - si vous ne pouvez pas ouvrir le fichier, il est en quelque sorte verrouillé.
*] sauf à root
processe.
(FWIW, lisant un fichier à moitié écrit sera entraîner une condition EOF.)
Soit cela est pris en charge sur votre système d'exploitation, soit ce n'est pas le cas. Si si ça, c'est facile et simple.
use Fcntl qw( O_CREAT O_EXCL O_WRONLY O_EXLOCK );
$creat_flags = (O_CREAT | O_EXCL | O_WRONLY | O_EXLOCK );
sysopen(SOMEHANDLE, $somepath, $creat_flags, 0666)
|| die "$0: couldn't sysopen $somepath with flags $creat_flags: $!";