Flock(): удаление заблокированного файла без условия гонки?

Я использую flock() для inter-process с именем mutexes (т.е. какой-то процесс может решить заблокировать "some_name", который реализуется путем блокировки файла с именем "some_name" в каталоге temp:

lockfile = "/tmp/some_name.lock";
fd = open(lockfile, O_CREAT);
flock(fd, LOCK_EX);

do_something();

unlink(lockfile);
flock(fd, LOCK_UN);

Файл блокировки должен быть удален в какой-то момент, чтобы избежать заполнения каталога temp сотнями файлов.

Однако в этом коде есть очевидное условие гонки; пример с процессами A, B и C:

A opens file
A locks file
B opens file
A unlinks file
A unlocks file
B locks file (B holds a lock on the deleted file)
C opens file (a new file one is created)
C locks file (two processes hold the same named mutex !)

Есть ли способ удалить файл блокировки в какой-то момент без представления этого состояния гонки?

Ответ 1

Извините, если я отвечу на мертвый вопрос:

После блокировки файла откройте другую копию, fstat обеих копий и проверьте номер inode, например:

lockfile = "/tmp/some_name.lock";

    while(1) {
        fd = open(lockfile, O_CREAT);
        flock(fd, LOCK_EX);

        fstat(fd, &st0);
        stat(lockfile, &st1);
        if(st0.st_ino == st1.st_ino) break;

        close(fd);
    }

    do_something();

    unlink(lockfile);
    flock(fd, LOCK_UN);

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

Я фактически доказал это в модели состояния-машины, используя следующие свойства:

Если P_i имеет дескриптор, заблокированный в файловой системе, тогда никакой другой процесс не находится в критическом разделе.

Если P_i после стата с правильным индексом или в критическом разделе, он имеет дескриптор, заблокированный в файловой системе.

Ответ 2

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

Для этого вам необходимо создать операцию, которая создает запись в каталоге и сообщает об ошибке, если она уже существует. В Linux и в большинстве файловых систем для этого будет работать O_EXCL до open. Но некоторые платформы и некоторые файловые системы (в частности, более старые NFS) не поддерживают это. Поэтому справочная страница для open предлагает альтернативу:

Портативные программы, которые хотят выполнять блокировку атомарного файла с использованием файла блокировки и должны избегать поддержки NFS для O_EXCL, могут создавать уникальный файл в той же файловой системе (например, включая имя хоста и PID) и использовать link (2), чтобы сделать ссылку на файл блокировки. Если link (2) возвращает 0, блокировка будет успешной. В противном случае используйте stat (2) в уникальном файле, чтобы проверить, увеличено ли его количество ссылок до 2, и в этом случае блокировка также успешный.

Таким образом, это выглядит как схема блокировки, которая официально задокументирована и, следовательно, указывает на определенный уровень поддержки и рекомендации по лучшей практике. Но я видел и другие подходы. bzr, например, использует каталоги вместо символических ссылок в большинстве мест. Цитата из его исходный код:

Блокировка представлена ​​на диске каталогом определенного имени, содержащий файл информации. Выполнение блокировки осуществляется путем переименования временный каталог. Мы используем временные каталоги, потому что для всех известных транспортов и файловых систем мы считаем, что ровно один попытка заявить, что замок будет успешным, а остальные потерпят неудачу. (файлы не будет, потому что некоторые файловые системы или транспорты переименовать и перезаписать, что затрудняет определение того, кто выиграл.)

Один недостаток вышеприведенных подходов заключается в том, что они не будут блокироваться: неудачная попытка блокировки приведет к ошибке, но не дожидаться, пока блокировка станет доступной. Вам нужно будет опросить блокировку, что может быть проблематичным в свете споров о блокировке. В этом случае вам может потребоваться дальнейшее отступление от подхода на основе файловой системы и вместо этого использовать сторонние реализации. Но общие вопросы о том, как сделать ipc-мьютекс, уже заданы, поэтому я предлагаю вам искать [ipc] [mutex] и посмотреть результаты, этот. Кстати, эти теги могут быть полезны и для вашего сообщения.