14.2.2.2. Установка и снятие блокировок

14.2.2.2. Установка и снятие блокировок

После заполнения структуры struct flock следующим шагом является запрос блокировки. Этот шаг осуществляется с помощью соответствующего значения аргумента cmd функции fcntl():

F_GETLK   Узнать, можно ли установить блокировку.

F_SETLK   Установить или снять блокировку.

F_SETLKW  Установить блокировку, подождав, пока это будет возможным.

Команда F_GETLK является командой «Мама, можно мне?» Она осведомляется, доступна ли описанная struct flock блокировка. Если она доступна, блокировка не устанавливается; вместо этого операционная система изменяет поле l_type на F_UNLCK. Другие поля остаются без изменений.

Если блокировка недоступна, операционная система заполняет различные поля сведениями, описывающими уже установленные блокировки, которые препятствуют установке новой. В этом случае l_pid содержит PID процесса, владеющего соответствующей блокировкой.[152] Если блокировка уже установлена, нет другого выбора, кроме ожидания в течение некоторого времени и новой попытки установки блокировки или вывода сообщения об ошибке и отказа от дальнейших попыток.

Команда F_SETLK пытается установить указанную блокировку. Если fcntl() возвращает 0, блокировка была успешно установлена. Если она возвращает -1, блокировку установил другой процесс. В этом случае в errno устанавливается либо EAGAIN (попытайтесь снова позже) или EACCESS (нет доступа). Возможны два значения, чтобы удовлетворить старым системам.

Команда F_SETLKW также пытается установить указанную блокировку. Она отличается от F_SETLK тем, что будет ждать, пока установка блокировки не окажется возможной.

Выбрав соответствующее значение для аргумента cmd, передайте его в качестве второго аргумента fcntl() вместе с указателем на заполненную структуру struct flock в качестве третьего аргумента:

struct flock lock;

 int fd;

 /* ...открыть файл, заполнить struct flock... */

 if (fcntl(fd, F_SETLK, &lock) < 0) {

 /* Установить не удалось, попытаться восстановиться */

}

Функция lockf()[153] предоставляет альтернативный способ установки блокировки в текущем положении файла.

#include <sys/file.h> /* XSI */

int lockf(int fd, int cmd, off_t len);

Дескриптор файла fd должен быть открыт для записи. len указывает число блокируемых байтов: от текущего положения (назовем его pos) до pos + len байтов, если len положительно, или от pos - len до pos - 1, если len отрицательно. Команды следующие:

F_LOCK   Устанавливает исключительную блокировку диапазона. Вызов блокируется до тех пор, пока блокировка диапазона не станет возможной.

F_TLOCK  Пытается установить блокировку. Это похоже на F_LOCK, но если блокировка недоступна, F_TLOCK возвращает ошибку.

F_ULOCK  Разблокирует указанный раздел. Это может вызвать расщепление блокировки, как описано выше.

F_TEST   Проверяет, доступна ли блокировка. Если доступна, возвращает 0 и устанавливает блокировку. В противном случае возвращает -1 и устанавливает в errno EACCESS.

Возвращаемое значение равно 0 в случае успеха и -1 при ошибке, с соответствующим значением в errno. Возможные значения ошибок включают:

EAGAIN Файл заблокирован, для F_TLOCK или F_TEST.

EDEADLK Для F_TLOCK эта операция создала бы тупик.[154]

ENOLCK Операционная система не смогла выделить блок.

Полезна комбинация F_TLOCK и EDEADLK: если вы знаете, что тупик не может возникнуть никогда, используйте F_LOCK. В противном случае, стоит обезопасить себя и использовать F_TLOCK. Если блокировка доступна, она осуществляется, но если нет, у вас появляется возможность восстановления вместо блокирования в ожидании, возможно, навечно.

Завершив работу с заблокированным участком, его следует освободить. Для fcntl() возьмите первоначальную struct lock, использованную для блокирования, и измените поле l_type на F_UNLCK. Затем используйте F_SETLK в качестве аргумента cmd:

lock.l_whence = ... ; /* Как раньше */

lock.l_start = ... ; /* Как раньше */

lock.l_len = ... ; /* Как раньше */

lock.l_type = F_UNLCK; /* Разблокировать */

if (fcntl(fd, F_SETLK, &lock) < 0) {

 /* обработать ошибку */

}

/* Блокировка была снята */

Код, использующий lockf(), несколько проще. Для краткости мы опустили проверку ошибок:

off_t curpos, len;

curpos = lseek(fd, (off_t)0, SEEK_CUR); /* Получить текущее положение */

len = ... ; / * Установить соответствующее число блокируемых байтов */

lockf(fd, F_LOCK, len); / * Осуществить блокировку */

/* ...здесь использование заблокированного участка... */

lseek(fd, curpos, SEEK_SET); / * Вернуться к началу блокировки */

lockf(fd, F_ULOCK, len); /* Разблокировать файл */

Если вы не освободите блокировку явным образом, операционная система сделает это за вас в двух случаях. Первый случай, когда процесс завершается (либо при возвращении из main(), либо с использованием функции exit(), которую мы рассматривали в разделе 9.1.5.1 «Определение статуса завершения процесса»). Другим случаем является вызов close() с дескриптором файла: больше об этом в следующем разделе.