Системные вызовы pread() и pwrite() работают практически так же, как read() и write(), за исключением того, что файловый ввод-вывод осуществляется с места, указанного значением offset, а не с текущего файлового смещения. Эти вызовы не изменяют файлового смещения.
#include
ssize_t pread(int
Возвращает количество считанных байтов, 0 при EOF или –1 при ошибке
ssize_t pwrite(int
Возвращает количество записанных байтов или –1 при ошибке
Вызов pread() эквивалентен
off_t orig;
orig = lseek(fd, 0, SEEK_CUR); /* Сохранение текущего смещения */
lseek(fd, offset, SEEK_SET);
s = read(fd, buf, len);
lseek(fd, orig, SEEK_SET); /* Восстановление исходного файлового смещения */
Как для pread(), так и для pwrite() файл, ссылка на который дается в аргументе fd, должен быть пригодным для изменения смещения (то есть представлен файловым дескриптором, в отношении которого допустимо вызвать lseek()).
В частности, такие системные вызовы могут пригодиться в многопоточных приложениях. В главе 29 будет показано, что все потоки в процессе совместно используют одну и ту же таблицу файловых дескрипторов. Это означает, что файловое смещение для каждого открытого файла является для всех потоков глобальным. Используя pread() или pwrite(), несколько потоков могут одновременно осуществлять ввод-вывод в отношении одного и того же файлового дескриптора, без влияния тех изменений, которые производят в отношении файлового смещения другие потоки. Если попытаться воспользоваться вместо этого lseek() плюс read() (или write()), то мы создадим состояние гонки, подобной одной из тех, описание которых давалось при рассмотрении флага O_APPEND в разделе 5.1. (Системные вызовы pread() и pwrite() могут также пригодиться для устранения состояния гонки в приложениях, когда у нескольких процессов имеются файловые дескрипторы, ссылающиеся на одну и ту же дескрипцию открытого файла.)
При условии многократного выполнения вызовов lseek() с последующим файловым вводом-выводом системные вызовы pread() и pwrite() могут также предложить в некоторых случаях преимущества в производительности. Дело в том, что отдельный системный вызов pread() (или pwrite()) приводит к меньшим издержкам, чем два системных вызова: lseek() и read() (или write()). Но издержки, связанные с системными вызовами, обычно незначительны по сравнению со временем фактического выполнения ввода-вывода.
Системные вызовы readv() и writev() выполняют фрагментированный ввод/вывод (scatter-gather I/O).
#include
ssize_t readv(int
Возвращает количество считанных байтов, 0 при EOF или –1 при ошибке
ssize_t writev(int
Возвращает количество записанных байтов или –1 при ошибке
За один системный вызов обрабатываются несколько таких буферов данных. Набор передаваемых буферов определяется массивом iov. Количество элементов в iov указывается в iovcnt. Каждый элемент в iov является структурой с такой формой:
struct iovec {
void *iov_base; /* Начальный адрес буфера */
size_t iov_len; /* Количество байтов для передачи в буфер или из него */
};
Согласно спецификации SUSv3, допускается устанавливать ограничение по количеству элементов в iov. Реализация может уведомить о своем ограничении, определив значение IOV_MAX в заголовочном файле
При этом функции оболочки из библиотеки glibc для readv() и writev() незаметно выполняют дополнительные действия. Если системный вызов дает сбой по причине слишком большого значения iovcnt, функция-оболочка временно выделяет один буфер, чьего объема достаточно для хранения всех элементов, описанных iov, и выполняет вызов read() или write() (см. далее тему о возможной реализации writev() с использованием write()).
На рис. 5.3 показан пример взаимосвязанности аргументов iov и iovcnt, а также буферов, на которые они ссылаются.