READV(2) Руководство программиста Linux READV(2)

ИМЯ

readv, writev, preadv, pwritev, preadv2, pwritev2 - читает или пишет данные в несколько буферов

ОБЗОР

#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);
ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt,
                off_t offset, int flags);
ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt,
                 off_t offset, int flags);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

preadv(), pwritev(): начиная с glibc 2.19: _DEFAULT_SOURCE glibc 2.19 и старее: _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() объединены возможности readv() и pread(2). Он выполняет ту же задачу что и readv(), но имеет четвёртый аргумент offset, задающий файловое смещение, по которому нужно выполнить операцию чтения.

В системном вызове pwritev() объединены возможности readv() и pwrite(2). Он выполняет ту же задачу что и writev(), но имеет четвёртый аргумент offset, задающий файловое смещение, по которому нужно выполнить операцию записи.

Файловое смещение не изменяется данными вызовами. Файл, заданный в fd, должен позволять изменение смещения.

Данные системные вызовы подобны preadv() и pwritev(), но имеют дополнительный пятый аргумент flags, который изменяет поведение в зависимости от вызова.

В отличие от preadv() и pwritev(), если аргумент offset равен -1, то текущее файловое смещение используется и обновляется.

Аргумент flags содержит побитовое ИЛИ нуля и более следующих флагов:

Эквивалентен флагу O_DSYNC для open(2), но действующий в конкретной операции записи. Этот флаг имеет смысл только для pwritev2(), и его действие распространяется только на диапазон данных, записываемым системным вызовом.
Высокоприоритетное чтение/запись. Позволяет в файловых системах на основе блоков использовать опрос устройства с низкой задержкой, но с возможностью использовать дополнительные ресурсы (в настоящий момент это свойство работает только для файлового дескриптора, открытого с флагом O_DIRECT).
Эквивалентен флагу O_SYNC для open(2), но действующий в конкретной операции записи. Этот флаг имеет смысл только для pwritev2(), и его действие распространяется только на диапазон данных, записываемым системным вызовом.
Не ждать данные, если пока нет. Если указан этот флаг, то системный вызов preadv2() сразу завершает работу, если бы ему пришлось читать данные из хранилища (backing storage) или ждать блокировку. Если данные прочитаны, то возвращается количество прочитанных байт. Если байты не прочитаны, то возвращается -1 и errno присваивается EAGAIN. В настоящее время, этот флаг учитывается только в preadv2().
Предоставляет эквивалент флага O_APPEND для open(2) для каждой записи. Данный флаг значим только для pwritev2(), и его действие проявляется только для диапазона данных, записанных системным вызовом. Аргумент offset не учитывается при операции записи; данные всегда добавляются в конец файла. Однако, если аргумент offset равен -1, то обновляется текущее смещение файла.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении readv(), preadv() и preadv2() возвращается количество считанных байт; вызовы writev(), pwritev() and pwritev2() возвращают количество записанных байт.

Заметим, что для успешного выполнения не считается ошибкой передача меньшего количества байт чем запрошено (смотрите read(2) и write(2)).

В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.

ОШИБКИ

Вызовы могут возвращать те же ошибки что и read(2) и write(2). Кроме этого, preadv(), preadv2(), pwritev() и pwritev2() также могут завершаться с ошибками как у lseek(2). Дополнительно определены следующие ошибки:

Сумма значений iov_len превышает значение ssize_t.
Количество векторов iovcnt меньше нуля или больше разрешённого максимума.
В flags указан неизвестный флаг.

ВЕРСИИ

Вызовы preadv() и pwritev() впервые появились в Linux 2.6.30; поддержка в библиотеке добавлена в glibc 2.10.

Вызовы preadv2() и pwritev2() впервые появились в Linux 4.6. Поддержка в библиотеке glibc добавлена в версии 2.26.

СООТВЕТСТВИЕ СТАНДАРТАМ

readv(), writev(): POSIX.1-2001, POSIX.1-2008, 4.4BSD (данные системные вызовы впервые появились в 4.2BSD).

preadv(), pwritev(): нет в стандарте, но есть в современных BSD.

preadv2(), pwritev2(): нестандартные расширения Linux.

ЗАМЕЧАНИЯ

Согласно 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 всегда просто вызывают системные вызовы.

ПРИМЕР

Следующий пример кода демонстрирует использование 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);

СМОТРИТЕ ТАКЖЕ

pread(2), read(2), write(2)

2018-04-30 Linux