70-散布读、聚集写

看到这个标题你可能会懵圈,没事,只是名字有点恐怖而已。在英文中,它们被称为 scatter read和 gather write.

1. 引例

看下面一段代码:

char buf1[10]; char buf2[20]; char buf3[15];  write(fd, buf1, 10); write(fd, buf2, 20); write(fd, buf3, 15);

上面这样的代码实际上很常见,不知道你是否还记得我们在讲解 TCP_NODELAY 套接字选项的时候,遇到过一种 write-write-read 的情形,和这个很类似。

接下来我想说的是,有没有一种办法,只执行一次 write 就可以把所有 buf 中的数据写入?最容易想到的就是将三个 buf 缓冲中的数据合并成一个 buf,一次写入。linux 为我们提供了另一个更加方便的函数——writev,它意为 write vector,即一次写入很多个 buf,比如可以这样:

writev(fd, buf1, 10, buf2, 20, buf3, 15);

当然啦,上面这种方法只是为了方便描述,实际上 writev 函数的参数是一个结构体数组。

writev 函数和 readv 函数原型:

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);  ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

这两个函数参数相同,下面只讲解 writev 函数的用法,readv 函数雷同。

2. writev 函数

  • 函数原型
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);  struct iovec {   void  *iov_base;    /* 缓冲区地址 */   size_t iov_len;     /* 要读写多少字节 */ };

writev 函数的第二个参数是一个 iovec 类型数组,第三个参数是数组的大小。

如果将第 1 节中的程序改为 writev 方式实现,应该像下面这样:

struct iovec iov[3]; iov[0].iov_base = buf1; iov[0].iov_len = 10;  iov[1].iov_base = buf2; iov[1].iov_len = 20;  iov[2].iov_base = buf3; iov[2].iov_len = 15;  writev(fd, iov, 3);

是不是非常简单?writev 有一个好处是它的操作是原子的,意味着它在内部会将所有缓冲区中的数据一次性发送出去,对于 UDP 来说,它只产生一个 UDP 数据报。

3. writev 函数示例

下面这段程序,将 buf 缓冲中的下标为偶数位置数据写入文件 filename 中。

void write_routine() {   int i, fd, nw;   struct iovec iov[10];   char buf[64];    strcpy(buf, "abcdefghijklmnopqrstuvwxyz");    for (i = 0; i < 10; ++i) {     iov[i].iov_base = buf + 2*i;     iov[i].iov_len = 1;   }    fd = open(filename, O_WRONLY | O_CREAT, 0666);   if (fd < 0) ERR_EXIT("open");    nw = writev(fd, iov, 10);   if (nw < 0) ERR_EXIT("writev"); }

最后,文件 filename 中的内容是:

acegikmoqs

4. 程序运行结果

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/advcio/readv_writev/rwv.cc.

程序 rwv 包含了 writev 和 readv 的例子,使用命令行参数控制。

  • readv 演示

readv 从文件 filename 中读取数据,并写入到 iovec 指定的缓冲区中。在此 demo 中,将数据定入到了 buf 缓冲区中下标为偶数的位置中。


这里写图片描述
图1 readv 演示结果

  • writev 演示

writev 函数示例和第 3 节中描述的一致。


这里写图片描述
图2 writev 函数演示

5. 总结

  • 掌握 writev 和 readv 函数

说明:本文转自blog.csdn.net,用于学习交流分享,仅代表原文作者观点。如有疑问,请联系我们删除~