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

jit*_*hsk 8 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调用中将所有内容写入文件?

R..*_*R.. 7

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

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

我不确定第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终端上操作时.


Har*_*son 5

知道代码如何工作的直接方法是阅读源代码.

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

它简单地将alloca()或malloc()作为缓冲区,将所有向量复制到其中,并调用write()一次.

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