4.15 高效的 readv 和 writev 函数

在实际开发种,高性能服务有一条尽量减少系统调用的原则。对于一个文件描述符(file descriptor,fd,例如文件指针、套接字类型)的 read 或者 write,都是系统调用。有时候我们会遇到通过一个文件描述对应的文件或套接字上将数据读到多个缓冲区中去,或者将多个缓冲区中的数据同时写入一个文件描述符对应的文件或者套接字中。当然,你可以通过多次调用 read 或者 write,挨个操作每个缓冲区。Linux 系统提供了一组 readv 和 writev 系列函数来完成上述工作。这个系列函数的签名如下:

#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);
1
2
3
4
5
6
7
8
9

参数 fdoffset 的含义都是不言自明的。参数 iov 是一个 struct iovec 数组,参数 iovcnt 是该数组元素的个数。函数 readvpreadv 调用成功返回总读取字节数,函数 writevpwritev 调用成功返回总写入字节数,函数调用失败均返回 -1。

struct iovec 定义如下:

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

字段 iov_base 是存放数据缓冲区的地址,字段 iov_len 是缓冲区的长度。

我们以 writev 函数为例来演示其用法,假设现在我们需要将 3 段数据同时写入一个 CSV 文件中,第一段数据是 CSV 的文件头信息,第二段和第三段是两只股票信息。示例代码如下:

char* headerInfo = "timestamp,symbol,side,volume,price\n";
char* firstSymbolInfo = "2014-12-12D06:53:11.127838000,600358,Buy,347,3.48\n";
char* secondSymbolInfo = "2014-12-12D06:53:11.127838000,600359,Buy,251,4.99\n";

struct iovec iov[3];
iov[0].iov_base = headerInfo;
iov[0].iov_len = strlen(headerInfo);
iov[1].iov_base = firstSymbolInfo;
iov[1].iov_len = strlen(firstSymbolInfo);
iov[2].iov_base = secondSymbolInfo;
iov[2].iov_len = strlen(secondSymbolInfo);

//fpCSV 为打开的CSV文件句柄
ssize_t nwritten = writev(fpCSV, iov, 2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

为了提高程序执行效率,读者可能会在很多 C/C++ 网络库中见到 readv 和 writev 函数的身影。

上次更新: 2025/04/01, 20:53:14

社区交流

讨论与留言

前往 GitHub Issues →

本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。

最近更新
第 1 课:专栏导论与安全边界
07-01
第 3 课:安装编译与调试环境
07-01
第 4 课:第三方库下载、编译与依赖管理
07-01
更多文章>