Есть ли способ создать файл, который заблокирован в момент создания, в Perl?

Мне нужно создать файл, который заблокирован от чтения, в момент создания, чтобы другие процессы, которые могут искать этот файл, не начали читать его до того, как он будет полностью записан.

Я знаю, что могу создать и затем заблокировать его, но я обеспокоен тем, что это оставляет меня открытым для состояния гонки.

Или я здесь ни о чем не беспокоюсь? Если у меня есть файл, открытый для записи, а затем открыть его для чтения другим процессом, будет ли процесс чтения никогда не видеть EOF, пока процесс записи не закроет файл?

3 ответа

Решение

Есть условие гонки с > а также >>, но это можно обойти, используя +<,

# >
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 $!;
...

В сценарии, который вы описываете, есть и состояние гонки.

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

Общая стратегия, чтобы избежать этой проблемы, состоит в том, чтобы иметь автора

  1. создайте файл во временном каталоге, а затем
  2. rename файл в каталог, который читатель отслеживает, когда файл завершен.

rename это атомарное действие, поэтому файл будет выглядеть полностью сформированным в каталоге, который отслеживает читатель. Это требует сотрудничества писателя, но лучшие решения будут.

Использование umask(0777) перед созданием файла.

Запись файла в файловой системе будет полностью недоступна [*] (т.е. права доступа ----------), даже если дескриптор файла, который у вас есть, разрешает запись.

затем chmod() файл, как только вы закончите:

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: на самом деле это не "блокировка" в строгом смысле, когда операционная система активно запрещает доступ к файлам. Это "хак" на уровне файловой системы - если вы не можете открыть файл, он будет заблокирован.

[*] кроме как root processe.

(FWIW, чтение наполовину записанного файла приведет к состоянию EOF.)

Либо это поддерживается в вашей операционной системе, либо нет. Если это так, то это легко и просто.

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: $!";
Другие вопросы по тегам