Обычно вызов fcntl() применяется для блокировки байтов в диапазоне, который соответствует границам записи внутри файла, определенным на уровне приложения; отсюда и термин
Стандарт SUSv3 требует, чтобы блокировка записей поддерживалась для обычных файлов, и допускает ее поддержку другими файловыми объектами. Как правило, эту блокировку имеет смысл применять только к обычным файлам (поскольку в случае с другими файловыми объектами понятие диапазона байтов теряет свое первоначальное значение), но в Linux это можно делать для любого файлового дескриптора.
На рис. 51.2 показано, как можно синхронизировать доступ двух процессов к одному и тому же участку файла, используя блокировку записей (предполагается, что все запросы выполняются в блокирующем режиме и приостанавливаются, если блокировка удерживается другим процессом).
Рис. 51.2.
В целом создание или удаление файловой блокировки с применением вызова fcntl() выглядит следующим образом:
struct flock flockstr;
/* Инициализируем поля 'flockstr', чтобы описать блокировку,
которую нужно установить или удалить */
fcntl(fd, cmd, &flockstr); /* Устанавливаем блокировку, описанную в 'fl' */
Аргумент fd представляет собой открытый дескриптор, ссылающийся на файл, который мы хотим заблокировать.
Прежде чем перейти к аргументу cmd, сначала познакомимся со структурой flock.
51.3.1. Структура flock
Структура flock описывает блокировку, которую нужно установить или снять. Она имеет следующее определение:
struct flock {
short l_type; /* Тип блокировки: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /* Как интерпретируется поле 'l_start': SEEK_SET,
SEEK_CUR, SEEK_END */
off_t l_start; /* Начало блокировки (сдвиг) */
off_t l_len; /* Количество блокируемых байтов; 0 означает «до конца файла» */
pid_t l_pid; /* Процесс, не дающий нам установить
блокировку (только для F_GETLK) */
};
Поле l_type обозначает тип блокировки, которую мы хотим установить. Оно может принимать одно из значение, перечисленных в табл. 51.3.
С точки зрения семантики блокировки, предназначенные для чтения (F_RDLCK) и записи (F_WRLCK), соответствуют разделяемым и эксклюзивным блокировкам, устанавливаемым вызовом flock(), и подчиняются тем же правилам совместимости (см. табл. 51.2). Блокировку F_RDLCK, относящуюся к определенному участку файла, может удерживать любое количество процессов, но только один процесс может владеть блокировкой F_WRLCK (которая к тому же исключает блокировки любых типов, принадлежащих другим процессам). Использование значения F_UNLCK для аргумента l_type аналогично операции LOCK_UN для вызова flock().
Таблица 51.3. Типы блокировок, создаваемые вызовом fcntl()
Тип блокировки — Описание
F_RDLCK — Устанавливает блокировку для чтения
F_WRLCK — Устанавливает блокировку для записи
F_UNLCK — Удаляет существующую блокировку
Для установки блокировки F_RDLCK файл должен быть открыт для чтения. Аналогично F_WRLCK требует, чтобы файл был открыт для записи. Для размещения блокировок обоих видов файл следует открывать в режиме чтения-записи (O_RDWR). Попытка установить блокировку, несовместимую с режимом доступа к файлу, приведет к ошибке EBADF.
Поля l_whence, l_start и l_len в совокупности определяют диапазон байтов, которые нужно заблокировать. Первые два из них являются аналогами аргументов whence и offset для вызова lseek() (см. раздел 4.7). Поле l_start обозначает сдвиг внутри файла, вычисляемый относительно:
• начала файла, если аргумент l_whence равен SEEK_SET;
• текущего сдвига в файле, если аргумент l_whence равен SEEK_CUR;
• конца файла, если аргумент l_whence равен SEEK_END.
В двух последних случаях аргумент l_start может иметь отрицательное значение, если итоговая позиция находится не перед началом файла (байтом 0).
Поле l_len содержит содержит целое число, обозначающее количество байтов, которые нужно заблокировать, начиная с позиции, заданной с помощью l_whence и l_start. Теоретически заблокировать можно и несуществующие байты, выходящие за пределы конца файла, но байты, размещаемые до его начала, не могут быть заблокированы.
Начиная с версии 2.4.21 ядро Linux позволяет передавать аргументу l_len отрицательные значения. Это значит, что заблокировать нужно l_len байт, размещенных до позиции, заданной с помощью l_whence и l_start (то есть байты в диапазоне от (l_start — abs(l_len)) до (l_start — 1)). Такая возможность не является обязательной, но допускается стандартом SUSv3. Она также поддерживается в некоторых других реализациях UNIX.