14.2.2.1. Описание блокировки

14.2.2.1. Описание блокировки

Прежде чем рассмотреть осуществление блокировки, давайте исследуем описание блокировки в операционной системе. Это делается при помощи структуры struct flock, которая описывает диапазон блокируемых байтов и вид нужной блокировки. Стандарт POSIX утверждает, что struct lock содержит «по крайней мере» определенные члены. Это позволяет разработчикам предоставлять при желании дополнительные члены структуры. Из слегка отредактированной справочной страницы fcntl(3):

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; /* PID блокирующего процесса (только F_GETLK) */

 ...

};

Поле l_start является смешением начального байта блокируемого участка. l_len является длиной блокируемого участка, т. е. общим числом блокируемых байтов. l_whence указывает место в файле, относительно которого отсчитывается l_start, значения те же, что и для аргумента whence функции lseek() (см раздел 4.5 «Произвольный доступ: перемещения внутри файла»), отсюда и название поля. Эта структура самодостаточна: смещение l_start и значение l_whence не связаны с текущим файловым указателем для чтения или записи. Пример кода мог бы выглядеть таким образом:

struct employee { /* что угодно */ }; /* Описание сотрудника */

struct flock lock; /* Структура блока */

...

/* Заблокировать структуру для шестого сотрудника */

lock.l_whence = SEEK_SET; /* Абсолютное положение */

lock.l_start = 5 * sizeof(struct employee); /* Начало 6-й структуры */

lock.l_len = sizeof(struct employee); /* Блокировать одну запись */

Используя SEEK_CUR или SEEK_END, вы можете заблокировать участки, начиная от текущего смещения в файле или относительно конца файла соответственно. Для этих двух случаев l_start может быть отрицательным, пока абсолютное начало не меньше нуля. Таким образом, чтобы заблокировать последнюю запись в файле:

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

lock.l_whence = SEEK_END; /* Относительно EOF */

lock.l_start = -1 * sizeof (struct employee);

 /* Начало последней структуры */

lock.l_len = sizeof(struct employee); /* Заблокировать одну запись */

Установка l_len в 0 является особым случаем. Он означает блокировку файла от начального положения, указанного с помощью l_start и l_whence, и до конца файла. Сюда входят также любые области за концом файла. (Другими словами, если заблокированный файл увеличивается в размере, область блокировки расширяется таким образом, чтобы продолжать охватывать весь файл.) Таким образом, блокирование всего файла является вырожденным случаем блокирования одной записи:

lock.l_whence = SEEK_SET; /* Абсолютное положение */

lock.l_start = 0; /* Начало файла */

lock.l_len = 0; /* До конца файла */

Справочная страница fnctl(3) имеет примечание:

POSIX 1003.1-2001 допускает отрицательные значения l_len. (И если это так, описываемый блоком интервал охватывает байты с l_start + l_len вплоть до l_start - 1 включительно.) Однако, в этой ситуации системный вызов Linux для современных ядер возвращает EINVAL.

(Мы заметили, что справочная страница относится к версиям ядер 2.4.x; стоит проверить текущую справочную страницу, если ваша система новее.)

Теперь, когда мы знаем, как описать где блокируется файл, мы можем описать тип блокировки с помощью l_type. Возможные значения следующие:

F_RDLCK  Блокировка чтения. Для применения блокировки чтения файл должен быть открыт для чтения.

F_WRLCK  Блокировка записи. Для применения блокировки записи файл должен быть открыт для записи.

F_UNLCK  Освобождение предыдущей блокировки.

Таким образом, полная спецификация блокировки включает установку в структуре struct flock значений четырех полей: трех для указания блокируемой области и четвертого для описания нужного типа блока.

Значение F_UNLCK для l_type снимает блокировку. В общем, это простейший способ снять те самые блоки, которые были установлены ранее, но можно «расщепить» блок, освободив диапазон байтов в середине ранее установленного более крупного блока. Например:

struct employee { /* что угодно */ }; /* Описание сотрудника */

struct flock lock; /* Структура блока */

...

/* Заблокировать сотрудников 6-8 */

lock.l_whence = SEEK_SET; /* Абсолютное положение */

lock.l_start = 5 * sizeof(struct employee); /* Начало 6-й структуры */

lock.l_len = sizeof(struct employee) * 3; /* Заблокировать 3 записи */

/* ...установка блокировки (см. следующий раздел)... */

/* Освобождение записи 7: предыдущий блок расщепляется на два: */

lock.l_whence = SEEK_SET; /* Абсолютное положение */

lock.l_start = 6 * sizeof(struct employee); /* Начало 7-й структуры */

lock.l_len = sizeof(struct employee) * 1; /* Разблокирование 1-й записи */

/* ...снятие блокировки (см. следующий раздел)... */