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
, такое решение не является особо эффективным. Вместо этого приложения могут перемещать все данные в последовательную область памяти, делая возможным один системный вызов. Однако эти действия приводят к множеству ненужных операций с памятью.
Linux предлагает системные вызовы
readv
и
writev
, реализующие разбросанное/сборное чтение и запись [98] . В отличие от стандартных элементов своего уровня, получающих по одному указателю и размеру буфера, эти системные вызовы получают массивы записей, каждая запись которых описывает буфер. Буферы читаются или записываются в том порядке, в каком они приведены в массиве. Каждый буфер описывается с помощью структуры
struct iovec
.
98
Они так именуются, поскольку чтение разбрасывает данные по всей памяти, а запись собирает данные из разных областей памяти. Они также известны как векторное чтение и запись. Этим объясняется наличие "v" в конце
readv
и
writev
.
#include <sys/uio.h>
struct iovec {
void * iov_base; /* адрес буфера */
size_t iov_len; /* длина буфера */
};
Первый элемент,
iov_base
, указывает на буферное пространство. Элемент
iov_len
— это количество символов в буфере. Эти элементы представляют собой то же, что и второй и третий параметры, передаваемые
read
и
write
.
Ниже показаны прототипы
readv
и
writev
.
#include <sys/uio.h>
int readv(int fd, const struct iovec * vector, size_t count);
int writev(int fd, const struct iovec * vector, size_t count);
Первый аргумент является файловым дескриптором, с которого можно считывать или на который можно записывать. Второй аргумент,
vector
, указывает на массив элементов
count struct iovec
. Обе функции возвращают общее количество прочитанных или записанных байтов.