Стоит отметить следующие моменты относительно установки и снятия блокировок, созданных с помощью вызова 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. В данной ситуации блокируется каждый второй запрос на получение блокировки; так происходит из-за блокировки, установленной другим процессом. Этот сценарий называется взаимной блокировкой. Теоретически он может привести к тому, что оба процесса будут заблокированы навсегда. Однако ядро учитывает такую возможность, проверяя каждый новый запрос на получение блокировки, сделанный операцией F_SETLKW, убеждаясь в том, что он не приведет к взаимной блокировке. Если опасность существует, то ядро выбирает один из заблокированных процессов и делает так, чтобы его вызов fcntl() был разблокирован и завершился с ошибкой EDEADLK. (В Linux выбирается процесс, который последним сделал вызов fcntl(), но такое поведение не является обязательным с точки зрения стандарта SUSv3 и может отличаться в других реализациях UNIX или будущих версиях Linux. Любой процесс, выполняющий операцию F_SETLKW, должен быть готов к обработке ошибки EDEADLK.)

Ядро способно обнаружить взаимную блокировку даже в ситуации, когда блокировки применяются к разным файлам, в результате чего процессы блокируются по кругу. (Это можно описать так: процесс А пытается получить блокировку для участка, заблокированного процессом Б; тот, в свою очередь, пытается получить блокировку, удерживаемую процессом В; а процесс В ждет получения блокировки, удерживаемой процессом А.)

51.3.5. Пример: программа для интерактивной блокировки

Программа, представленная в листинге 51.2, является интерактивной и позволяет экспериментировать с блокировкой записей. Она принимает всего один аргумент командной строки — имя файла, который мы хотим заблокировать. С ее помощью можно проверить многие приведенные выше утверждения относительно того, как работает частичная блокировка. Данная программа предназначена для интерактивного использования и принимает команды следующего вида:

cmd lock start length [whence ]

Рис. 51.4. Взаимная блокировка двух процессов, которые не дают друг другу выполнить запрос на получение блокировки

Перейти на страницу:

Похожие книги