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: }
13.4. Альтернативы read()
и write()
Несмотря на то что системные вызовы read()
и write()
как нельзя лучше подходят приложениям для извлечения и хранения данных в файле, все же они не всегда являются самыми быстрыми методами. Они допускают управление отдельными порциями данных; для записи же нескольких порций данных требуется несколько системных вызовов. Подобным образом, если приложению необходим доступ к данным в разных частях файла, оно должно вызывать lseek()
между каждым read()
или write()
, удваивая количество необходимых системных вызовов. Для улучшения эффективности существуют другие системные вызовы.
13.4.1. Разбросанное/сборное чтение и запись
Приложениям часто требуется читать и записывать данные различных типов в последовательные области файла. Несмотря на то что это можно делать сравнительно легко с помощью множества вызовов read()
и write()
, такое решение не является особо эффективным. Вместо этого приложения могут перемещать все данные в последовательную область памяти, делая возможным один системный вызов. Однако эти действия приводят к множеству ненужных операций с памятью.
96
Если один сигнал используется для аренды множества файлов, убедитесь, что сигнал является сигналом реального времени, так что множество событий аренды ставятся в очередь. Если используется обычный сигнал, он может потеряться либо события аренды могут возникать через очень короткие промежутки времени.
97
До тех пор пока O_NONBLOCK
не будет определен как флаг open()
; в этом случае возвращается EWOULDBLOCK
.