Почему для File::FcntlLock l_type всегда F_UNLCK, даже если файл заблокирован?

Приведенная ниже подпрограмма Perl использует File::FcntlLock для проверки того, заблокирован ли файл.

Почему он возвращает 0 и печатает /tmp/test.pid is unlocked., даже если файл заблокирован?

sub getPidOwningLock {
    my $filename = shift;

    my $fs = new File::FcntlLock;
    $fs->l_type( F_WRLCK );
    $fs->l_whence( SEEK_SET );
    $fs->l_start( 0 );
    $fs->l_len( 0 );

    my $fd;
    if (!open($fd, '+<', $filename)) {
        print "Could not open $filename\n";
        return -1;
    }

    if (!$fs->lock($fd, F_GETLK)) {
        print "Could not get lock information on $filename, error: $fs->error\n";
        close($fd);
        return -1;
    }

    close($fd);

    if ($fs->l_type() == F_UNLCK) {
        print "$filename is unlocked.\n";
        return 0;
    }

    return $fs->l_pid();
}

Файл заблокирован следующим образом (lock.sh):

#!/bin/sh
(
    flock -n 200
    while true; do sleep 1; done
) 200>/tmp/test.pid

Файл действительно заблокирован:

~$ ./lock.sh &
[2] 16803
~$ lsof /tmp/test.pid
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash    26002 admin  200w   REG    8,5        0 584649 test.pid
sleep   26432 admin  200w   REG    8,5        0 584649 test.pid

person Saulo Silva    schedule 15.02.2017    source источник
comment
Не знаю, но flock($fd,6) показывает, что файл заблокирован.   -  person mob    schedule 15.02.2017
comment
Разве это не то, что говорится в документах, которые вы получаете с F_GETLK? С F_GETLK метод lock() определяет, удерживает ли процесс блокировку в данный момент и какой именно. Если другой блокировки нет, для свойства l_type будет установлено значение F_UNLCK.   -  person ThisSuitIsBlackNot    schedule 15.02.2017
comment
@ThisSuitIsBlackНеправильно. Я считаю, что вы неправильно прочитали мой вопрос. Код Perl не должен блокироваться.   -  person Saulo Silva    schedule 15.02.2017
comment
Но почему бы ему не закрыться? flock не то же самое, что fcntl. См. man(2) flock: Начиная с ядра 2.0, flock() реализован как системный вызов. сам по себе, а не эмулируется в библиотеке GNU C как вызов fcntl(2). Это дает истинную семантику BSD: нет никакого взаимодействия между типами блокировок, устанавливаемых flock() и fcntl(2)...   -  person ThisSuitIsBlackNot    schedule 15.02.2017
comment
См. также: В чем разница между блокировкой с помощью fcntl и flock.   -  person    schedule 15.02.2017
comment
@ThisSuitIsBlackNot: я думаю, вы должны опубликовать это как ответ.   -  person Borodin    schedule 15.02.2017
comment
@Borodin Я чувствовал, что могу неправильно понять, чего хотел ОП, поэтому я ждал, пока они пояснят. Но теперь Wumpus Q. Wumbley написал хороший, подробный ответ.   -  person ThisSuitIsBlackNot    schedule 15.02.2017
comment
Связано: О неэффективности блокировки файлов   -  person ThisSuitIsBlackNot    schedule 15.02.2017
comment
Я разрывался между написанием ответа и поиском основной причины вопроса. Если целью была синхронизация некоторых сценариев Perl, а сценарий оболочки был просто проверкой, то более простой ответ: выйти из использования сценария оболочки, чтобы проверить это.   -  person    schedule 15.02.2017


Ответы (1)


Блокировки fcntl и flock невидимы друг для друга.

Это большая проблема для вашего варианта использования, потому что утилита flock, которую вы используете в своем сценарии оболочки, зависит от семантики flock: сценарий оболочки запускает дочерний процесс flock, который блокирует унаследованный дескриптор файла, а затем завершает работу. Оболочка держит этот файловый дескриптор открытым (поскольку перенаправление выполняется для целой последовательности команд) до тех пор, пока не захочет снять блокировку.

Этот план не может работать с fcntl, потому что fcntl блокировки не распределяются между процессами. Если бы существовала утилита, идентичная flock, но использующая fcntl, блокировка была бы снята слишком рано (как только завершается дочерний процесс).

Для координации блокировки файла между процессом Perl и сценарием оболочки вы можете рассмотреть следующие варианты:

  • перенесите сценарий оболочки на zsh и используйте встроенный zsystem flock из модуля zsh/system (примечание: в документации утверждается, что он использует fcntl, несмотря на то, что его имя flock)
  • переписать сценарий оболочки на perl
  • просто используйте flock в сценарии perl (откажитесь от блокировки диапазона байтов и функции «получить PID шкафчика», но вы можете эмулировать это в Linux, прочитав /proc/locks)
  • напишите свою собственную утилиту fcntl на C для использования в сценарии оболочки (схема использования будет другой - сценарий оболочки должен будет запустить его в фоновом режиме, а затем убить его позже, чтобы разблокировать - и ему потребуется какой-то способ сообщить родительскому процессу, когда он получил или не смог получить блокировку, что будет сложно, потому что теперь это происходит асинхронно... возможно, используйте функцию coprocess, которая есть в некоторых оболочках).
  • запустите небольшой скрипт Perl из сценария оболочки, чтобы выполнить блокировку (потребуется та же фоновая обработка, что и для специальной утилиты fcntl)

Дополнительные сведения о функциях различных типов блокировок см. в разделе В чем разница между блокировкой с помощью fcntl и flock.

person Community    schedule 15.02.2017
comment
Спасибо за исчерпывающий ответ. Я решил переписать сценарий оболочки на Perl/File::FcntlLock, так как он использовался только для тестирования. - person Saulo Silva; 15.02.2017