13.3.2. Блокировка записей

13.3.2. Блокировка записей

С целью преодоления проблем, присущих блокировочным файлам, в System V и BSD 4.3 была добавлена блокировка записей, реализуемая с помощью системных вызовов lockf() и flock() соответственно. Стандарт POSIX определил третий механизм для блокировки записей, который использует системный вызов fcntl(). Хотя Linux поддерживает все три интерфейса, мы обсудим только интерфейс POSIX, поскольку сейчас его поддерживают почти все платформы Unix. Кроме того, функция lockf() реализована как интерфейс для fcntl(), поэтому оставшаяся часть данного обсуждения касается обоих методов.

Существуют два значительных отличия между блокировками записей и блокировочными файлами. Во-первых, блокировки записей применяются к произвольной части файла. Например, процесс А может заблокировать байты с 50-го по 200-й файла, в то время как другой процесс блокирует байты с 2500-го по 3000-й без конфликта двух блокировок. Мелкомодульное блокирование полезно, когда нескольким процессам необходимо обновить один файл. Еще одно преимущество блокирования записей заключается в том, что блокировки удерживаются в ядре, а не в файловой системе. По окончании процесса все блокировки, которые он содержит, освобождаются.

Как и блокировочные файлы, блокировки POSIX также являются рекомендательными. Linux, как и System V, предоставляет обязательный вариант блокирования записей, который можно использовать, но нарушая при этом переносимость. Блокирование файлов может работать или не работать в сетевой файловой системе (NFS). В последних версиях Linux блокирование файлов работает в NFS, если на всех машинах, участвующих в блокировке, выполняется демон блокировки NFS lockd.

При блокировке записей используются два типа блокировок: блокировка чтения и блокировка записи. Блокировки чтения также известны как разделяемые блокировки, поскольку несколько процессов могут одновременно удерживать блокировки чтения на одной и той же области. Для нескольких процессов чтение структуры данных, которая не обновляется, безопасно всегда. Когда процессу необходимо записать файл, ему понадобится блокировка записи (или эксклюзивная блокировка). Только один процесс может удерживать блокировку на определенной записи, и пока блокировка записи существует, блокировки чтения не допускаются. Это гарантирует, что процесс не повлияет на модули чтения, пока будет осуществлять запись в область.

Множество блокировок одного процесса никогда не конфликтуют друг с другом[90].

Если процесс имеет блокировку чтения на байтах 200—250 и пытается установить блокировку записи на байты 200–225, ему это удастся. Исходная блокировка смещается и становится блокировкой чтения на байтах 226–250, а новая блокировка записи устанавливается на байты 200–225[91]. Это позволяет предотвратить взаимоблокировку одного процесса (хотя ситуация взаимоблокировки нескольких процессов по-прежнему возможна).

Блокирование записей POSIX осуществляется с помощью системного вызова fcntl(). В главе 11 было показано, что fcntl() выглядит следующим образом.

#include <fcntl.h>

int fcntl (int fd, int command, long arg);

Для всех операций блокировки третий параметр (arg) указывает на структуру struct flock, представленную ниже.

#include <fcntl.h>

struct flock {

 short l_type;

 short l_whence;

 off_t l_start;

 off_t l_len;

 pid_t l_pid;

};

Первый элемент, l_type, определяет тип установленной блокировки.

F_RDLCK Устанавливается блокировка чтения (разделяемая).
F_WRLCK Устанавливается блокировка записи (эксклюзивная).
F_UNLCK Снимается существующая блокировка.

Следующие два элемента, l_whence и l_start, определяют начало области тем же способом, что и файловые смещения, передаваемые в lseek(). l_whence сообщает о способе интерпретации l_start и принимает одно из значений SEEK_SET, SEEK_CUR или SEEK_END; более подробно эти значения рассматривались в главе 11. Следующий элемент, l_len, сообщает размер блокировки в байтах. Если l_len равно 0, считается, что блокировка распространяется до конца файла. Последний элемент, l_pid, используется только тогда, когда запрашиваются блокировки. Он устанавливается в идентификатор процесса, владеющего запрашиваемой блокировкой.

Существуют три команды fcntl(), относящиеся к блокировке файла. Они передаются fcntl() во втором аргументе, fcntl() возвращает -1 в случае ошибки и 0 — в противном случае. Ниже перечислены допустимые значения параметра command.

F_SETLK Устанавливает блокировку, описанную в arg. Если блокировку невозможно выдать из-за конфликта с блокировками других процессов, возвращается EAGAIN. Если l_type устанавливается в F_UNLCK, существующая блокировка снимается.
F_SETLKW Подобно F_SETLK, но блокирует только при условии предоставления блокировки. Если сигнал поступает во время блокирования процесса, вызов fcntl() возвращает EAGAIN.
F_GETLK Проверяет возможность выдачи описанной в arg блокировки. Если блокировка предоставляется, содержимое struct flock не меняется, кроме l_type, который устанавливается в F_UNLCK. Если блокировка не выдается, l_pid устанавливается в идентификатор процесса, содержащего конфликтующую блокировку. Значение 0 возвращается независимо от того, будет ли предоставлена блокировка.

Хотя F_GETLK позволяет процессу проверить, будет ли выдана блокировка, следующий код все еще не сможет получить блокировку.

fcntl(fd, F_GETLK, &lockinfo);

if (lockinfо.l_type != F_UNLCK) {

 fprintf(stderr, "конфликт блокировок ");

 return 1;

}

lockinfо.l_type = F_RDLCK;

fcntl(fd, F_SETLK, &lockinfo);

Другой процесс мог заблокировать область между двумя вызовами fcntl(), приводя к тому, что второму вызову fcntl() не удается установить блокировку.

В качестве простого примера блокировки записей ниже приведена программа, которая открывает файл, устанавливает на нем блокировку чтения, освобождает блокировку чтения, устанавливает блокировку записи и закрывается. В промежутках между каждым из этих шагов программа ожидает, пока пользователь нажмет клавишу <Enter>. Если получить блокировку не удается, программа отображает идентификатор процесса, содержащего конфликтующую блокировку, и запрашивает у пользователя о необходимости повторить попытку. Запуск этой программы на двух терминалах облегчит экспериментирование с правилами блокировок POSIX.

 1: /* lock.с */

 2:

 3: #include <errno.h>

 4: #include <fcntl.h>

 5: #include <stdio.h>

 6: #include <unistd.h>

 7:

 8: /* выводит сообщение и ожидает нажатия

 9:    пользователем клавиши <Enter> */

10: void waitforuser(char * message) {

11:  char buf[10];

12:

13:  printf("%s", message);

14:  fflush(stdout);

15:

16:  fgets(buf, 9, stdin);

17: }

18:

19: /* Получает блокировку заданного типа на файловом дескрипторе fd.

20:    Типом блокировки может быть F_UNLCK, F_RDLCK или F_WRLCK */

21: void getlock(int fd, int type) {

22:  struct flock lockinfo;

23:  char message[80];

24:

25:  /* будет блокироваться весь файл */

26:  lockinfo.l_whence = SEEK_SET;

27:  lockinfo.l_start = 0;

28:  lockinfo.l_len = 0;

29:

30:  /* продолжать попытки, пока того желает пользователь */

31:  while (1) {

32:   lockinfo.l_type = type;

33:   /* если блокировка получена, немедленно возвратиться */

34:   if (!fcntl(fd, F_SETLK, &lockinfo)) return;

35:

36:   /* найти, кто удерживает конфликтующую блокировку */

37:   fcntl(fd, F_GETLK, &lockinfo);

38:

39:   /* есть шанс, что блокировка освобождена между F_SETLK

40:      и F_GETLK; проверить, существует ли еще конфликт

41:      перед тем, как сообщать об этом */

42:   if (lockinfo.l_type != F_UNLCK) {

43:    sprintf (message, "конфликт с процессом %d... нажмите "

44:     "<enter> для повторения:", lockinfo.l_pid);

45:    waitforuser(message);

46:   }

47:  }

48: }

49:

50: int main(void) {

51:  int fd;

52:

53:  /* подготовить файл для блокировки */

54:  fd = open("testlockfile", O_RDWR | O_CREAT, 0666);

55:  if (fd < 0) {

56:   perror("open");

57:   return 1;

58:  }

59:

60:  printf("получение блокировки чтения ");

61:  getlock(fd, F_RDLCK);

62:  printf("блокировка чтения получена ");

63:

64:  waitforuser(" для продолжения нажмите <enter>:");

65:

66:  printf("освобождение блокировки ");

67:  getlock(fd, F_UNLCK);

68:

69:  printf("получение блокировки записи ");

70:  getlock(fd, F_WRLCK);

71:  printf("блокировка записи получена ");

72:

73:  waitforuser(" для завершения нажмите <enter>:");

74:

75:  /* при закрытии файла блокировки освобождаются */

76:

77:  return 0;

78: }

С блокировками следует обращаться иначе, чем с другими файловыми атрибутами. Блокировки ассоциируются с парой (pid, inode), в отличие от многих атрибутов открытых файлов, которые связаны с файловым дескриптором или файловой структурой. Следовательно, если процесс выполняет перечисленные ниже действия, то файл уже не блокируется процессом.

1. Открытие одного файла дважды, что дает два разных файловым дескриптора.

2. Поучение блокировки чтения на одной области в обоих файловых дескрипторах.

3. Закрытие одного из файловых дескрипторов.

Выдана лишь одна блокировка чтения, потому что была задействована только одна пара (pid, inode) (вторая попытка блокировки удалась, поскольку блокировки одного и того же процесса никогда не конфликтуют), и после закрытия одного из файловых дескрипторов у процесса больше нет блокировок на файле.

После fork() родительский процесс сохраняет свои файловые блокировки, но дочерний процесс — нет. Если бы дочерние процессы наследовали блокировки, два процесса пришли бы, в конечном счете, к блокировке записи на одной области файла.

Однако файловые блокировки наследуются в exec(). Поскольку в POSIX не определено, что происходит с блокировками после exec(), все варианты Unix сохраняют их[92].

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Блокировка слоев

Из книги AutoCAD 2009 для студента. Самоучитель автора Соколова Татьяна Юрьевна

Блокировка слоев Блокировку слоев полезно применять в случаях, когда требуется редактирование объектов, расположенных на определенных слоях, с возможностью просмотра объектов на других слоях. Редактировать объекты на заблокированных слоях нельзя. Однако они остаются


Блокировка слоев

Из книги AutoCAD 2009. Начали! автора Соколова Татьяна Юрьевна

Блокировка слоев Блокировку слоев полезно применять в случаях, когда требуется редактирование объектов, расположенных на определенных слоях, с возможностью просмотра объектов на других слоях. Редактировать объекты на заблокированных слоях нельзя. Однако они остаются


Блокировка

Из книги Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT автора Фролов Александр Вячеславович

Блокировка В состав класса CFile включены методы LockRange и UnlockRange, позволяющие заблокировать один или несколько фрагментов данных файла для доступа других процессов. Если приложение пытается повторно блокировать данные, уже заблокированные раньше этим или другим


7.4. Блокировка и ожидание

Из книги UNIX: взаимодействие процессов автора Стивенс Уильям Ричард

7.4. Блокировка и ожидание Продемонстрируем теперь, что взаимные исключения предназначены для блокирования, но не для ожидания. Изменим наш пример из предыдущего раздела таким образом, чтобы потребитель запускался сразу же после запуска всех производителей. Это даст


9.4. Рекомендательная блокировка

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

9.4. Рекомендательная блокировка Блокировка записей по стандарту Posix называется рекомендательной. Ядро хранит информацию обо всех заблокированных различными процессами файлах, но оно не предотвращает запись в заблокированный на чтение процесс. Ядро также не


9.5. Обязательная блокировка

Из книги Программирование на языке Ruby [Идеология языка, теория и практика применения] автора Фултон Хэл

9.5. Обязательная блокировка Некоторые системы предоставляют возможность установки блокировки другого типа — обязательной (mandatory locking). В этом случае ядро проверяет все вызовы read и write, блокируя их при необходимости. Если для дескриптора установлен флаг O_NONBLOCK, вызов read или


Блокировка записей fcntl

Из книги QNX/UNIX [Анатомия параллелизма] автора Цилюрик Олег Иванович

Блокировка записей fcntl Последняя пpoгрaммa использует fcntl для синхронизации. Функция main приведена в листинге А.30. Эта программа будет выполняться успешно только в том случае, если количество потоков равно 1, поскольку блокировка fcntl предназначена для использования между


15.17.4 Блокировка файлов

Из книги AutoCAD 2008 для студента: популярный самоучитель автора Соколова Татьяна Юрьевна

15.17.4 Блокировка файлов К некоторым файлам могут одновременно обратиться несколько пользователей. Например, конфигурационные файлы могут читаться несколькими процессами. Для изменения совместно используемого файла пользователь должен получить специальные полномочия


10.1.6. Блокировка файлов

Из книги Реестр Windows 7 автора Климов Александр Петрович

10.1.6. Блокировка файлов В тех операционных системах, которые поддерживают такую возможность, метод flock класса File блокирует или разблокирует файл. Вторым параметром может быть одна из констант File::LOCK_EX, File::LOCK_NB, File::LOCK_SH, File::LOCK_UN или их объединение с помощью оператора ИЛИ.


Ждущая блокировка

Из книги Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ автора Борри Хелен

Ждущая блокировка QNX предоставляет упрощенный вариант использования условной переменной для блокирования (остановки) потока при помощи интерфейса так называемой ждущей блокировки (sleepon). Для использования этого механизма не нужно явно создавать никаких объектов


Блокировка слоев

Из книги Linux программирование в примерах автора Роббинс Арнольд

Блокировка слоев Блокировку слоев полезно применять в случаях, когда требуется редактирование объектов, расположенных на определенных слоях, с возможностью просмотра объектов на других слоях. Редактировать объекты на заблокированных слоях нельзя. Однако они остаются


Блокировка SuperFetch

Из книги Linux и UNIX: программирование в shell. Руководство разработчика. автора Тейнсли Дэвид

Блокировка SuperFetch Функция SuperFetch позволяет ускорить работу системы за счет использования части оперативной памяти для кэширования данных, используемых при загрузке ОС и запуске приложений.В реестре за настройки функции отвечает ключ


Блокировка строки

Из книги автора

Блокировка строки В MGA наличие ожидающих завершения новых версий строки имеет следствием блокировку строки. При большинстве условий наличие новой подтвержденной версии блокирует запрос на изменение или удаление этой строки - это конфликт блокировки.При получении


Пессимистическая блокировка

Из книги автора

Пессимистическая блокировка В пессимистической блокировке СУБД строки, запрошенные одним пользователем или транзакцией для операции, которая может изменить состояние данных, немедленно становятся недоступными для чтения или записи другим пользователям или


14.2. Блокировка файлов

Из книги автора

14.2. Блокировка файлов Современные системы Unix, включая GNU/Linux, дают вам возможность заблокировать часть файла или весь файл для чтения или записи. Подобно многим частям Unix API, которые были разработаны после V7, имеется несколько несовместимых способов осуществить блокировку