曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4948|回复: 0
打印 上一主题 下一主题

[文件I/O] readv()/writev() -- 向量I/O读写

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2012-12-6 19:47:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
平时里用的 read()/write() 是线性 I/O 读写函数。所谓线性,就是一段缓冲区可以被连续的读写,而即将介绍的 readv()/writev() 称为向量 I/O 读写函数,也可以叫做 “分散/聚 集"(Scatter/gather) I/O 函数。这两个函数可以一次性对多个不连续的缓冲区进行读写,支持这种特性的一个关键结构是:
[C++] 纯文本查看 复制代码
 struct iovec {
               void  *iov_base;    /* Starting address */
               size_t iov_len;     /* Number of bytes to transfer */
           };

它定义在 <sys/uio.h> 头文件中。readv() 和 writev() 的原型为:
[C++] 纯文本查看 复制代码
#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);

函数有 3 个参数,
第 1 个参数 fd 是一个文件描述符,如果是 readv() 则是从 fd 中读取内容存储到缓冲中;如果是 writev() 则是将缓冲中的内容写到缓冲中。
第 2 个参数 iov 是上面所提到的 struct iovec 结构指针。这个结构有两个成员,其中 iov_base 表示某个缓冲区的起始地址,iov_len 表示该段缓冲区的长度。读写多个缓冲区就是由该结构来连结的。
第 3 个参数 iovcnt 表示多少个缓冲区。

用两段简单的代码也许更容易说明这两个函数的应用:

writev.c
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>


int main()
{
        struct iovec iov[3];
        ssize_t nr;
        int fd, i;




        char *buf[] = {".net", "groad", "www."};




        fd = open ("groad.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);
        if (fd == -1) {
                perror ("open");
                return 1;
        }


        /* fill out three iovec structures */
        for (i = 0; i < 3; i++) {
                iov[i].iov_base = buf[i];
                iov[i].iov_len = strlen(buf[i]);
                printf ("%d : %d\n", i, iov[i].iov_len);
        }


        /* write a single call, write them all out */
        nr = writev (fd, iov, 3);
        if (nr == -1) {
                perror("writev");
                return 1;
        }
        printf ("wrote %d bytes\n", nr);


        if (close(fd)) {
                perror("close");
                return 1;
        }


        return 0;
}

这个函数将缓冲区 buf 中的内容写到 groad.txt 这个文件中,运行输出:
./writev
0 : 4
1 : 5
2 : 4
wrote 13 bytes
接着,我们用 readv.c 这个程序将 groad.txt 这个文件中内容一次读出,readv.c 代码如下:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <strings.h>
int main()
{
        char foo[4];
        char bar[5];
        char baz[4];


        struct iovec iov[3];
        ssize_t nr;
        int fd, i;


        fd = open ("groad.txt", O_RDONLY);
        if (fd == -1) {
                perror ("open");
                return 1;
        }


        /* set up our iovec structures */
        iov[0].iov_base = foo;
        iov[0].iov_len = sizeof(foo);
        iov[1].iov_base = bar;
        iov[1].iov_len = sizeof(bar);
        iov[2].iov_base = baz;
        iov[2].iov_len = sizeof(baz);


        /* read into the structures with a single call */
        nr = readv (fd, iov, 3);
        if (nr == -1) {
                perror ("readv");
                return 1;
        }


        for (i = 0; i < 3; i++) {
                printf ("%s", (char *)iov[i].iov_base);
                printf ("\n");
        }


        if (close(fd)) {
                perror ("close");
                return 1;
        }


        return 0;
}

运行输出:
./readv
.net
groad.net
www.groad.net
向量I/O的读写的特点有:
1. 读入缓冲区的内容要依次填满缓冲区,即先填满 iov[0],接着是 iov[1], 然后是 iov[2] ... ...直到 iov[n-1] 。
2. 如果直接输出 iov[k] ,那么它会连续输出 iov[k], iov[k-1], ... ...iov[1], iov[0] 。

所以,根据第 2 条,在上面的 readv() 中输出 iov[2] 时,看上去逆序得到 www.groad.net 。

此外,向量 I/O 的一个特性是可以原子性读写,不会被其它的进程所干扰,并且它的性能比线性 I/O 要高一些,因为不必要调用多次系统调用。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 11:01 , Processed in 0.078175 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表