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 содержит побитовое ИЛИ нуля и более следующих флагов:
При успешном выполнении readv(), preadv() и preadv2() возвращается количество считанных байт; вызовы writev(), pwritev() and pwritev2() возвращают количество записанных байт.
Заметим, что для успешного выполнения не считается ошибкой передача меньшего количества байт чем запрошено (смотрите read(2) и write(2)).
В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.
Вызовы могут возвращать те же ошибки что и read(2) и write(2). Кроме этого, preadv(), preadv2(), pwritev() и pwritev2() также могут завершаться с ошибками как у lseek(2). Дополнительно определены следующие ошибки:
Вызовы 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.
Объявления системных вызовов preadv() и pwritev() немного отличаются от им соответствующих обёрточных функций библиотеки GNU C; они показаны в ОБЗОРЕ. Последний аргумент, offset, раскладывается обёрточными функциями на два для системных вызовов:
unsigned long pos_l, unsigned long pos
В этих аргументах содержатся старшая и младшая 32-битная часть offset, соответственно.
Для учёта того, что значение 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);
2018-04-30 | Linux |