READV - Linux手册页

时间:2019-08-20 17:59:08  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-06-09

名称

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()系统调用从与文件描述符fd相关联的文件中读取iovcnt缓冲区到iov描述的缓冲区中("分散输入")。

writev()系统调用将iov描述的数据的iovcnt缓冲区写入与文件描述符fd相关联的文件("聚集输出")。

指针iov指向iovec结构的数组,定义为:

struct iovec {
    void  *iov_base;    /* Starting address */
    size_t iov_len;     /* Number of bytes to transfer */
};

readv()系统调用的工作方式与read(2)相同,只是要填充多个缓冲区。

除了要写出多个缓冲区外,writev()系统调用的工作方式与write(2)相同。

缓冲区以数组顺序处理。这意味着readv()在进入iov [1]之前会完全填充iov [0],依此类推。 (如果数据不足,则可能不会填充iov指向的所有缓冲区。)类似地,writev()在进行iov [1]之前写出iov [0]的全部内容,依此类推。

readv()和writev()执行的数据传输是原子的:由writev()写入的数据是作为一个单独的块写入的,不会与其他进程中写入的输出混合在一起(但请参阅pipe(7)作为例外) ;类似地,无论在其他线程中执行的读取操作还是具有引用同一打开文件描述的文件描述符的进程,都确保readv()从文件中读取连续的数据块(请参阅open(2))。

preadv() and pwritev()

preadv()系统调用结合了readv()和pread(2)的功能。它执行与readv()相同的任务,但是添加了第四个参数offset,该参数指定要在其上执行输入操作的文件偏移量。

pwritev()系统调用结合了writev()和pwrite(2)的功能。它执行与writev()相同的任务,但是添加了第四个参数offset,该参数指定要在其上执行输出操作的文件偏移量。

这些系统调用不会更改文件偏移量。 fd引用的文件必须能够搜索。

preadv2() and pwritev2()

这些系统调用类似于preadv()和pwritev()调用,但是添加了第五个参数flags,该参数在每次调用的基础上修改行为。

preadv()和pwritev()不同,如果offset参数为-1,则使用和更新当前文件偏移。

flags参数包含零个或多个以下标志的按位或:

RWF_DSYNC(since Linux 4.7)
提供与O_DSYNC open(2)标志相同的每次写入功能。该标志仅对pwritev2()有意义,其作用仅适用于系统调用写入的数据范围。
RWF_HIPRI(since Linux 4.6)
高优先级读/写。允许基于块的文件系统使用设备的轮询,这可以降低延迟,但可能会使用其他资源。 (当前,此功能仅在使用O_DIRECT标志打开的文件描述符上可用。)
RWF_SYNC(since Linux 4.7)
提供等效于O_SYNC open(2)标志的每次写入功能。该标志仅对pwritev2()有意义,其作用仅适用于系统调用写入的数据范围。
RWF_NOWAIT(since Linux 4.14)
不要等待无法立即获得的数据。如果指定了此标志,则preadv2()系统调用将不得不从后备存储中读取数据或等待锁定时立即返回。如果成功读取了某些数据,它将返回读取的字节数。如果未读取任何字节,它将返回-1并将errno设置为EAGAIN。当前,此标志仅对preadv2()有意义。
RWF_APPEND(since Linux 4.16)
提供等效于O_APPEND open(2)标志的每次写入功能。该标志仅对pwritev2()有意义,其作用仅适用于系统调用写入的数据范围。 offset参数不影响写操作;数据总是附加在文件末尾。但是,如果offset参数为-1,则将更新当前文件的偏移量。

返回值

成功后,readv(),preadv()和preadv2()返回读取的字节数; writev(),pwritev()和pwritev2()返回写入的字节数。

请注意,成功调用传输的字节数少于请求的字节数不是错误(请参见read(2)和write(2))。

如果出错,则返回-1,并正确设置errno。

错误说明

错误如read(2)和write(2)所示。此外,出于与lseek(2)相同的原因,preadv(),preadv2(),pwritev()和pwritev2()也会失败。此外,定义了以下错误:

EINVAL
iov_len值的总和溢出ssize_t值。
EINVAL
向量计数iovcnt小于零或大于允许的最大值。
EOPNOTSUPP
在标志中指定了未知标志。

版本

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扩展。

备注

POSIX.1允许实现对可以在iov中传递的项目数进行限制。实现可以通过在运行时或在运行时通过sysconf(_SC_IOV_MAX)的返回值定义IOV_MAX来公布其限制。在现代Linux系统上,该限制为1024。在Linux 2.0天中,该限制为16。

C library/kernel differences

原始preadv()和pwritev()系统调用的调用签名与"摘要"中显示的相应GNU C库包装函数的签名略有不同。包装函数将最后一个参数offset打包为系统调用中的两个参数:

无符号长pos_l,无符号长pos

这些自变量分别包含偏移量的低阶和高阶32位。

Historical C library/kernel differences

为了解决Linux早期版本中IOV_MAX太低的问题,如果readv()和writev()的glibc包装函数检测到底层内核系统调用由于超出了此限制而失败,则它们做了一些额外的工作。对于readv(),包装函数为iov指定的所有项目分配了一个足够大的临时缓冲区,在对read(2)的调用中传递了该缓冲区,将数据从缓冲区复制到了iov_base指定的位置iov元素的字段,然后释放缓冲区。 writev()的包装函数使用临时缓冲区和对write(2)的调用来执行类似任务。

Linux 2.2及更高版本不再需要glibc包装函数中的这些额外工作。但是,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)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/