Стоит отметить следующие моменты относительно установки и снятия блокировок, созданных с помощью вызова fcntl().
• Разблокировка участка файла всегда завершается успешно и без задержек, даже если этот участок не содержал блокировки.
• В любой заданный момент времени процесс может удерживать только одну блокировку, относящуюся к определенному участку файла. Установка новой блокировки для уже заблокированного участка либо ничего не изменит (при совпадении типов новой и существующей блокировок), либо автоматически переведет существующую блокировку в другой режим. Во втором случае, если режим меняется с чтения на запись, нужно быть готовым к тому, что такой вызов может завершиться ошибкой (F_SETLK) или заблокироваться (F_SETLKW). Этим fcntl() отличается от функции flock(), в которой преобразование блокировок не является атомарной операцией.
• Процесс не может освободить себя от блокировки участка файла, даже если попытается установить блокировки через разные дескрипторы, ссылающиеся на один и тот же файл (этим вызов fcntl() отличается от функции flock(); мы еще вернемся к данной проблеме в подразделе 51.3.5).
• Установка блокировки, находящейся в другом режиме, посреди уже заблокированного участка приведет к получению трех разных блокировок: по обе стороны от новой блокировки появляются два участка меньше размера, сохраняющие предыдущий режим (рис. 51.3). И наоборот: получение второй блокировки в том же режиме, которая является смежной с уже существующей или перекрывает ее, приводит к созданию одной объединенной блокировки, покрывающей оба заблокированных участка. Возможны и другие вариации. Например, разблокировка небольшого диапазона посреди заблокированного участка приводит к созданию двух блокировок меньшего размера по обе стороны от этого диапазона. Если новая блокировка перекрывает существующую и имеет другой режим, то «захватывает» ее байты, уменьшая ее размер.
• Закрытие файлового дескриптора в случае с заблокированными участками файла имеет несколько необычную семантику, которая будет описана в подразделе 51.3.5.
Рис. 51.3.
51.3.4. Взаимная блокировка
Используя флаг F_SETLKW, нужно учитывать возможность сценария, проиллюстрированного на рис. 51.4. В данной ситуации блокируется каждый второй запрос на получение блокировки; так происходит из-за блокировки, установленной другим процессом. Этот сценарий называется
Ядро способно обнаружить взаимную блокировку даже в ситуации, когда блокировки применяются к разным файлам, в результате чего процессы блокируются по кругу. (Это можно описать так: процесс А пытается получить блокировку для участка, заблокированного процессом Б; тот, в свою очередь, пытается получить блокировку, удерживаемую процессом В; а процесс В ждет получения блокировки, удерживаемой процессом А.)
51.3.5. Пример: программа для интерактивной блокировки
Программа, представленная в листинге 51.2, является интерактивной и позволяет экспериментировать с блокировкой записей. Она принимает всего один аргумент командной строки — имя файла, который мы хотим заблокировать. С ее помощью можно проверить многие приведенные выше утверждения относительно того, как работает частичная блокировка. Данная программа предназначена для интерактивного использования и принимает команды следующего вида:
Рис. 51.4.