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

最佳答案

根据标准,由于以下几个原因,您提到的for循环不是writev的有效实现:

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

  • 我不确定第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实现将是单个syscall。深入了解如何在Linux上实现它:对于普通文件和大多数设备,底层文件驱动程序具有直接实现iov-style io的方法,而没有任何内部循环。但是Linux上的终端驱动程序已经过时并且缺乏现代的io方法,导致在终端上运行时内核回退到writev/readv的写/读循环。

    关于c - 列表I/O writev在内部如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9336572/

    10-11 19:36