zoukankan      html  css  js  c++  java
  • Unix系统编程():分散输入和集中输出(Scatter-Gather IO):readv和writev

    分散输入和集中输出(Scatter-Gather IO):readvwritev

     

    请问这个v又代表什么?

     

    readv和writev系统调用分别实现了分散输入和集中输出的功能。

     

    #include<sys/uio.h>

     

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

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

     

    这些系统调用并非只对单个缓冲区进行读写操作,而是一次即可传输多个缓冲区的数据。数组iov定义了一组用来传输数据的缓冲区。整型数iovcnt指定了iov的成员个数。iov中的每个成员都是如下形式的数据结构。

     

    struct iovec {

        void *iov_base;    /* Start address of buffer */

        size_t iov_len;       /* Number of bytes to transfer to/from buffer */

    };

     

     

    SUSv3标准允许系统实现对iov中的成员个数加以限制。系统实现可以通过定义<limits.h>文件中的IOV_MAX来通告这一限额,程序也可以在系统运行时调用sysconf(_SC_IOV_MAX)来获取这一限额。SUSv3要求该限额不得少于16。Linux将IOV_MAX的值定义为1024,这是与内核对该向量大小的限制(由内核常量UIO_MAXIOV定义)相对应的。

     

    然而,glibc对readv和writev的封装函数还悄悄做了些额外的工作。若系统调用因iovcnt参数值过大而失败,外壳函数将临时分配一块缓冲区,其大小足以容纳iov参数所有成员所描述的数据缓冲区,随后再执行read或write调用。

     

     

     

    分散输入

     

    readv系统调用实现了分散输入的功能:从文件描述符fd所指代的文件中读取一片连续的字节,然后将其散置(分散放置)于iov指定的缓冲区中。这一散置动作从iov[0]开始,依次填满每个缓冲区。

     

    原子性是readv的重要属性。换言之,从调用进程的角度来看,当调用readv时,内核在fd所指代的文件和用户内存之间一次性完成了数据转移。这意味着,假设即使有另一进程(线程)与其共享同一文件偏移量,且在调用readv的同时企图修改文件的偏移量,readv所读区的数据仍将是连续的。

     

    调用readv成功将返回读区的字节数,若文件结束将返回0。调用者必须对返回值进行检查,以验证读取的字节数是否满足要求。若数据不足以填充所有缓冲区,则是会占用部分的缓冲区,其中最后一个缓冲区可能只有部分数据。

     

    分散输出的用途在哪儿呢?

     

     

    集中输出

     

    writev系统调用实现了集中输出:将iov所指定的所有缓冲区中的数据拼接(集中)起来,然后以连续的字节序列写入文件描述符fd指代的文件中。对缓冲区中数据的集中始于iov[0]所指定的缓冲区,并按数组顺序展开。

     

    readv调用一样,writev调用也属于原子操作,即所有的数据将一次性的从用户内存传输到fd指代的文件中。因此,在向普通文件写入数据时,writev调用会把所有的请求数据连续写入到文件中,而不会在其他进程(或线程)写操作的影响下分散地写入文件。

     

    如同write调用,writev调用也存在部分写的问题。因此,必须检查writev调用的返回值,以确定写入的字节数是否与要求相符。

     

    readv调用和writev调用的主要优势在于便捷。如下两种方案,任选其一都可替代对writev的调用。

     

    编码时,首先分配一个大缓冲区,随即在从进程地址空间的其他位置将数据复制过来,最后调用write输出其中所有的数据。

     

    发起一列write调用,逐一输出每个缓冲区中的数据。

     

    尽管方案一在语义上等同于writev调用,但仍需在用户空间内分配缓冲区,进行数据复制,很不方便(小女也低)。

     

    方案二在语义上就不同于单次的writev调用,因为发起多次write调用无法保证原子性。更何况,执行一次writev调用要比执行多次write调用开销要小。

     

     

    在指定的文件偏移量处执行分散输入/集中输出

     

    Linux 2.6.30版本新增了两个系统调用preadv、pwritev,将分散输入/集中供暖输出和指定文件偏移量处的IO二者集于一身。它们并非标准的系统调用,但获得了现代BSD的支持。

  • 相关阅读:
    HDU5772 (最小割)
    HDU 4971 (最小割)
    暑期集训个人赛1
    HDU 5644 (费用流)
    HDU5619 (费用流)
    暑假集训热身赛
    构建之法阅读笔记05
    找小水王
    找水王
    Runner站立会议之个人会议(冲刺二)
  • 原文地址:https://www.cnblogs.com/tuhooo/p/8657324.html
Copyright © 2011-2022 走看看