readv - чтение или запись данных в несколько
readv(2)
чтение или запись данных в несколько
Other Alias
writev, preadv, pwritev
ОБЗОР
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,
off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,
off_t offset);
Требования макроса тестирования свойств для glibc
(см. feature_test_macros(7)):
preadv(), pwritev(): _BSD_SOURCE
ОПИСАНИЕ
Системный вызов
readv() считывает
iovcnt буферов из файла, связанного
с файловым дескриптором
fd, в буферы, описываемые
iov («разнесённый
ввод»).
Системный вызов writev() записывает iovcnt буферов, описанных iov,
в файл, связанный с файловым дескриптором fd («сборный вывод»).
Указатель iov указывает на массив структур iovec (определён в
<sys/uio.h>:
struct iovec {
void *iov_base; /* начальный адрес */
size_t iov_len; /* количество передаваемых байт */
};
Системный вызов readv() работает также как read(2), но считывает
несколько буферов.
Системный вызов writev() работает также как write(2), но записывает
несколько буферов.
Буферы выбираются в порядке, в каком они указаны в массиве. Это означает,
что readv() сначала полностью заполнит iov[0], и только потом перейдёт
к iov[1], и так далее. (Если данных недостаточно, то могут быть заполнены
не все буферы, на которые указывает iov.) Подобным образом writev()
запишет сначала всё содержимое iov[0], затем iov[1], и так далее.
Выполняемые вызовами readv() и writev() пересылки данных атомарны:
данные записываются writev() единичным блоком, который не перемешивается
с выводом других процессов (см. исключения в pipe(7)); аналогично,
readv() гарантированно считывает непрерывный блок данных из файла,
независимо от операций чтения из других нитей или процессов, которые имеют
файловые дескрипторы, ссылающиеся на это же открытое файловое описание
(см. open(2)).
preadv() и pwritev()
В системном вызове
preadv() объединены возможности
readv() и
pread(2). Он выполняет ту же задачу что и
readv(), но имеет четвёртый
аргумент
offset, задающий файловое смещение, по которому нужно выполнить
операцию чтения.
В системном вызове pwritev() объединены возможности readv() и
pwrite(2). Он выполняет ту же задачу что и writev(), но имеет
четвёртый аргумент offset, задающий файловое смещение, по которому нужно
выполнить операцию записи.
Файловое смещение не изменяется данными вызовами. Файл, заданный в fd,
должен позволять изменение смещения.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении
readv() и
preadv() возвращается количество
считанных байт; вызовы
writev() и
pwritev() возвращают количество
записанных байт.
Заметим, что для успешного выполнения не считается ошибкой передача меньшего
количества байт чем запрошено (смотрите read(2) и write(2)).
В случае ошибки возвращается -1 и значение errno устанавливается
соответствующим образом.
ОШИБКИ
Вызовы могут возвращать те же ошибки что и
read(2) и
write(2). Кроме
этого,
preadv() и
pwritev() также могут завершаться с ошибками как
lseek(2). Дополнительно, определены следующие ошибки:
EINVAL
Сумма значений iov_len превышает значение ssize_t.
EINVAL
Количество векторов iovcnt меньше нуля или больше разрешённого максимума.
ВЕРСИИ
Вызовы
preadv() и
pwritev() впервые появились в Linux 2.6.30;
поддержка в библиотеке добавлена в glibc 2.10.
СООТВЕТСТВИЕ СТАНДАРТАМ
readv(),
writev(): POSIX.1-2001, POSIX.1-2008, 4.4BSD (данные
системные вызовы впервые появились в 4.2BSD).
preadv(), pwritev(): нет в стандарте, но есть в современных BSD.
ЗАМЕЧАНИЯ
Согласно POSIX1, в реализации можно устанавливать ограничение на количество
элементов, которые можно передать в
iov. Реализация может объявить это
ограничение в
IOV_MAX (в файле
<limits.h>) или во время
выполнения в виде возвращаемого значения
sysconf(_SC_IOV_MAX). В
современных Linux данное ограничение равно 1024. В времена Linux 2.0 оно
было равно 16.
Отличия между библиотекой C и ядром
Объявления системных вызовов
preadv() и
pwritev() немного отличаются
от им соответствующих обёрточных функций библиотеки GNU C; они показаны в
ОБЗОРЕ. Последний аргумент,
offset, раскладывается обёрточными функциями
на два для системных вызовов:
unsigned long pos_l, unsigned long pos
В этих аргументах содержатся старшая и младшая 32-битная часть offset,
соответственно.
Исторические отличия между библиотекой C и ядром
Для учёта того, что значение
IOV_MAX было мало в старых версиях Linux,
обёрточные функции glibc
readv() и
writev() выполняют дополнительные
действия, если обнаруживается, что используемый системный вызов ядра
завершился неудачно из-за превышения этого ограничения. В случае
readv(),
обёрточная функция выделяет временный буфер, достаточный для всех элементов,
указанных в
iov, передаёт этот буфер в вызов
read(2), копирует данные
из буфера в места, указанные в полях
iov_base элемента
iov, а затем
освобождает буфер. Обёрточная функция
writev() выполняет аналогичную
задачу с помощью временного буфера и вызова
write(2).
Потребность в дополнительных действиях в обёрточных функциях glibc пропала в
Linux 2.2 и новее. Однако glibc продолжала так работать до версии
2.10. начиная с glibc 2.9, обёрточные функции так работают только, если
библиотека обнаруживает, что система работает с ядром Linux меньше 2.6.18
(произвольно выбранная версия ядра). И начиная с glibc 2.20 (для которой
требуется минимальная версия ядра Linux 2.6.32) обёрточные функции glibc
всегда просто вызывают системные вызовы.
Неразумно смешивать вызовы readv() или writev(), работающих с
дескрипторами файлов, вместе с функциями из библиотеки stdio; результат
непредсказуем и точно не тот, которого вы ожидаете.
ПРИМЕР
Следующий пример кода демонстрирует использование
writev():
char *str0 = "hello ";
char *str1 = "world\n";
struct iovec iov[2];
ssize_t nwritten;
iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);
nwritten = writev(STDOUT_FILENO, iov, 2);