【问题标题】:How does list I/O writev internally work?list I/O writev 如何在内部工作?
【发布时间】:2012-03-09 08:15:25
【问题描述】:

writev 函数将 struct iovec 数组作为输入参数

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

输入是需要写入文件的内存缓冲区列表(例如)。我想知道的是:

writev 内部是否这样做:

for (each element in iov) write(element)

这样iov 的每个元素都在单独的 I/O 调用中写入文件?还是writev单个 I/O 调用中将所有内容写入文件?

【问题讨论】:

    标签: c io posix


    【解决方案1】:

    根据标准,您提到的 for 循环不是 writev 的有效实现,原因如下:

    1. 如果写入时间较短,则循环可能无法完成写入一个 iov,然后再继续执行下一个 iov,但可以通过使循环更精细来解决此问题。
    2. 对于管道的原子性,循环可能有不正确的行为:如果总写入长度小于PIPE_BUF,则管道写入必须是原子的,但循环会破坏原子性要求。无法解决此问题,除非在总长度最多为 PIPE_BUF 时在写入之前将所有 iov 条目移动到单个缓冲区中。
    3. 循环可能会导致阻塞,其中单个writev 调用将需要执行部分写入而不阻塞。据我所知,这个问题在一般情况下是不可能解决的。
    4. 可能还有其他我没想到的原因。

    我不确定第 3 点,但在阅读时它肯定存在于相反的方向。如果终端有一些可用的数据(短于 iov 总长度)后跟 EOF 指示符,则在循环中调用 read 可能会阻塞;在这种情况下,调用readv应该立即返回部分读取。然而,由于 Linux 中的一个错误,终端上的readv 实际上是作为内核空间中的read 循环实现的,并且它确实存在这个阻塞错误。在实现 musl 的 stdio 时,我不得不解决这个错误:

    http://git.etalabs.net/cgi-bin/gitweb.cgi?p=musl;a=commit;h=2cff36a84f268c09f4c9dc5a1340652c8e298dc0

    回答你问题的最后一部分:

    或者writev 是否在一次 I/O 调用中将所有内容写入文件?

    在所有情况下,符合 writev 的实现将是单个系统调用。深入了解它在 Linux 上的实现方式:对于普通文件和大多数设备,底层文件驱动程序具有直接实现 iov 样式 io 的方法,没有任何内部循环。但是 Linux 上的终端驱动程序非常过时并且缺乏现代 io 方法,导致内核在终端上运行时回退到 writev/readv 的写入/读取循环。

    【讨论】:

    • 我不明白最后一行“在终端上操作时”。另外,您在 linux src 中的哪个位置检查 writev 实现?
    • “在终端上操作时”是指文件描述符指的是终端设备。至于源码在哪里,lxr.linux.no/#linux+v3.2.6/fs/read_write.c#L809
    【解决方案2】:
    Or does writev write everything to file in a single I/O call?
    

    虽然 sys_writev 尽最大努力在一次调用中编写所有内容,但我并不担心所有内容。这取决于 vfs 的实现,如果 vfs 没有提供 writev 的实现,则 kenerl 将在循环中调用 vfs 的 write()。最好检查 writev/readv 的返回值,看看你在 write() 中写了多少字节。

    你可以在内核中找到writev的代码,fs/read_write.c:do_readv_writev。

    【讨论】:

      【解决方案3】:

      了解代码如何工作的直接方法是阅读源代码。

      http://www.oschina.net/code/explore/glibc-2.9/sysdeps/posix/writev.c

      它只是简单地 alloca() 或 malloc() 一个缓冲区,将所有向量复制到其中,然后调用 write() 一次。

      它是如何工作的。没什么神秘的。

      【讨论】:

      • 那是 glibc writev,不是 linux kernel writev。
      猜你喜欢
      • 1970-01-01
      • 2011-09-25
      • 2014-05-17
      • 2017-06-08
      • 1970-01-01
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 2016-07-29
      相关资源
      最近更新 更多