Выбрать главу

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("получение блокировки чтения\n");

61:  getlock(fd, F_RDLCK);

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

63:

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

65:

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

67:  getlock(fd, F_UNLCK);

68:

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

70:  getlock(fd, F_WRLCK);

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

72:

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

74:

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

76:

77:  return 0;

78: }

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

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

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

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

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

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

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

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

И Linux, и System V поддерживают как обычные, так и обязательные блокировки. Обязательные блокировки устанавливаются и реализуются с помощью того же механизма fcntl(), который используется для рекомендательной блокировки записей. Блокировки считаются обязательными, если бит setgid заблокированного файла установлен, но его бит группового выполнения — нет. В противном случае применяется рекомендательное блокирование.

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

Обязательное блокирование записи приводит к большей потере производительности, чем рекомендательное блокирование, поскольку каждый вызов read() и write() должен быть проверен на предмет конфликтов с блокировками. Оно также не настолько переносимо, как рекомендательное блокирование POSIX, поэтому в большинстве приложений обязательное блокирование применять не следует.

13.3.4. Аренда файла

И рекомендательное, и обязательное блокирование предназначены для предотвращения доступа процесса к файлу или его части, которая используется другим процессом. Когда блокировка установлена, процесс, которому необходим доступ к файлу, должен подождать завершения процесса, владеющего блокировкой. Эта структура подходит для большинства применений, но иногда программа использует файл до тех пор, пока он не понадобится другой программе, и желает получить при необходимости эксклюзивный доступ к файлу. Для этого Linux предлагает механизм аренды файлов (в других системах это называется периодическими блокировками (oplocks))[93].

Взятие файла в аренду позволяет процессу получать уведомления (через сигнал) о доступе к файлу со стороны другого процесса. Существуют два типа аренды: аренда чтения и аренда записи. Аренда чтения вызывает передачу сигнала при открытии файла для записи, открытии с указанием O_TRUNC или вызове truncate(). Аренда записи также посылает сигнал при открытии файла для чтения[94]22. Аренды файлов работают только для модификаций, внесенных в файл той же системой, которая владеет арендой. Если файл локальный (не файл, доступ к которому возможен через сеть), любой подходящий доступ к файлу инициирует сигнал. Если доступ к файлу возможен через сеть, передачу сигнала вызывают только процессы на одной машине с процессо-арендатором; доступ с любой другой машины удается в случае отсутствия аренды.

Системный вызов fcntl() используется для создания, реализации и запроса аренды файлов. Аренды можно размещать только на обычных файлах (для каналов и каталогов это невозможно), кроме того, аренды записи предоставляются только владельцу файла. Первый аргумент fcntl() — это интересующий для слежения файловый дескриптор, а второй аргумент, command, определяет, какую операцию следует выполнить.

F_SETLEASE

Аренда создается или освобождается в зависимости от значения последнего параметра, передаваемого в fcntl(); F_RDLCK создает аренду чтения, F_WRLCK — аренду записи, a F_UNLCK освобождает любую аренду, которая может существовать. Если запрашивается новая аренда, она заменяет любую существующую аренду. В случае ошибки возвращается отрицательное число; ноль или положительное число свидетельствуют об успехе операции[95].

вернуться

92

Эффект, производимый вызовами fork() и exec() на файловые блокировки, является наиболее существенным отличием между файловой блокировкой POSIX (а, следовательно, и блокировкой lockf()) и файловой блокировкой flock() в BSD.

вернуться

93

Одним из самых частых пользователей аренды файлов является файловый сервер Samba, позволяющий клиентам кэшировать свои записи для увеличения производительности.

вернуться

94

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

вернуться

95

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