Книга: Разработка приложений в среде Linux. Второе издание

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

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].

F_GETLEASE

Возвращается тип аренды, существующей в настоящий момент для файла (F_RDLCK, F_WRLCK или F_UNLCK).

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

Использование F_SETSIG дает один значительный эффект. По умолчанию siginfo_t не передается обработчику при доставке SIGIO. Если используется F_SETSIG, даже когда сигналом, передаваемым в ядро, является SIGIO, a SA_SIGINFO был установлен при регистрации обработчика сигнала, файловый дескриптор, аренда которого инициировала событие, передается в обработчик сигналов одновременно с элементом siginfo_t по имени si_fd. Это позволяет применять отдельный сигнал к аренде множества файлов, в то время как si_fd сообщает сигналу, какому файлу необходимо уделить внимание[96].

Единственные два системных вызова, которые могут инициировать передачу сигнала для арендуемого файла — это open() и truncate(). Когда они вызываются процессом для арендуемого файла, они блокируются[97], и процессу-владельцу передается сигнал, open() или truncate() завершаются после удаления аренды с файла (или его закрытия процессом-владельцем, что вызывает удаление аренды). Если процесс, удерживающий аренду, не отменяет снятие в течение времени, указанного в файле /proc/sys/fs/lease-break-time, ядро прерывает аренду и позволяет завершиться запускающему системному вызову.

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

 1: /* leases.с */
 2:
 3: #define GNU_SOURCE
 4:
 5: #include <fcntl.h>
 6: #include <signal.h>
 7: #include <stdio.h>
 8: #include <string.h>
 9: #include <unistd.h>
10:
11: const char ** fileNames;
12: int numFiles;
13:
14: void handler (int sig, siginfo_t * siginfo, void * context) {
15:  /* Когда аренда истекает, вывести сообщение и закрыть файл.
16:     Предполагается, что первый открываемый файл получит файловый
17:     дескриптор 3, следующий - 4 и так далее. */
18:
19:  write(1, "освобождение", 10);
20:  write(1, fileNames[siginfo->si_fd - 3],
21:  strlen(fileNames[siginfo->si_fd - 3]));
22:  write(1, "n", 1);
23:  fcntl(siginfo->si_fd, F_SETLEASE, F_UNLCK);
24:  close(siginfo->si_fd);
25:  numFiles--;
26: }
27:
28: int main(int argc, const char ** argv) {
29:  int fd;
30:  const char ** file;
31:  struct sigaction act;
32:
33:  if (argc < 2) {
34:   fprintf(stderr, "использование: %s <filename>+n", argv[0]);
35:   return 1;
36:  }
37:
38:  /* Зарегистрировать обработчик сигналов. Указав SA_SIGINFO, предоставить
39:     обработчику возможность узнать, какой файловый дескриптор имеет
40:     истекшую аренду. */
41:  act.sa_sigaction = handler;
42:  act.sa_flags = SA_SIGINFO;
43:  sigemptyset(&act.sa_mask);
44:  sigaction(SIGRTMIN, &act, NULL);
45:
46:  /* Сохранить список имен файлов в глобальной переменной, чтобы
47:     обработчик сигналов мог иметь доступ к нему. */
48:  fileNames = argv + 1;
49:  numFiles = argc - 1;
50:
51:  /* Открыть файлы, установить используемые сигнал
52:     и создать аренду */
53:  for (file = fileNames; *file; file++) {
54:   if ((fd = open(* file, O_RDONLY)) < 0) {
55:    perror("open");
56:    return 1;
57:   }
58:
59:   /* Для правильного заполнения необходимо использовать F_SETSIG
60:      для структуры siginfo */
61:   if (fcntl(fd, F_SETSIG, SIGRTMIN) < 0) {
62:    perror("F_SETSIG");
63:    return 1;
64:   }
65:
66:   if (fcntl(fd, F_SETLEASE, F_WRLCK) < 0) {
67:    perror("F_SETLEASE");
68:    return 1;
69:   }
70:  }
71:
72:  /* Пока файлы остаются открытыми, ожидать поступления сигналов. */
73:  while (numFiles)
74:   pause();
75:
76:  return 0;
77: }

Оглавление книги


Генерация: 4.836. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз