列表I / O writev内部如何工作?

时间:2012-02-17 22:52:24

标签: c io posix

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调用中的文件?

3 个答案:

答案 0 :(得分:7)

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

  1. 如果写短文,循环可能无法完成写入一个iov然后继续写下一个iov - 但这可以通过使循环更精细来解决。
  2. 关于管道的原子性,循环可能有不正确的行为:如果总写入长度小于PIPE_BUF,则管道写入必须是原子的,但循环会破坏原子性要求。此问题无法解决,除非在写入之前将所有iov条目移动到单个缓冲区中,而总长度最多为PIPE_BUF
  3. 循环可能存在导致阻塞的情况,其中单个writev调用将需要执行部分写入而不会阻塞。据我所知,在一般情况下,这个问题是不可能解决的。
  4. 可能是我没有想过的其他原因。
  5. 我不确定第3点,但在阅读时肯定存在于相反的方向。在循环中调用read可能会阻止终端是否有一些可用的数据(短于总的iov长度),然后是EOF指示符;在这种情况下,调用readv 立即返回部分阅读。但是,由于Linux中的错误,终端上的readv实际上是在内核空间中实现为read循环,并且它确实表现出这种阻塞错误。我不得不在实现musl的stdio时解决这个bug:

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

    回答问题的最后部分:

      

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

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

答案 1 :(得分:5)

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

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

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

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

答案 2 :(得分:3)

Or does writev write everything to file in a single I/O call?

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

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