writev
函数接受一个 struct iovec 数组作为输入参数
writev(int fd, const struct iovec *iov, int iovcnt);
输入是需要写入文件(比方说)的内存缓冲区列表。我想知道的是:
writev 内部是否这样做:
for(iov中的每个元素)
写入(元素)
iov
的每个元素都在单独的 I/O 调用中写入文件?还是 writev
在单个 I/O 调用中将所有内容写入文件?
最佳答案
根据标准,您提到的 for 循环不是 writev
的有效实现,原因如下:
- 在短写的情况下,循环可能无法在继续下一个 iov 之前完成写入一个 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
回答问题的最后一部分:
Or does
writev
write everything to file in a single I/O call?
在所有情况下,符合要求的 writev
实现将是单个系统调用。深入了解它在 Linux 上的实现方式:对于普通文件和大多数设备,底层文件驱动程序具有直接实现 iov 样式 io 的方法,没有任何类型的内部循环。但是 Linux 上的终端驱动程序非常过时并且缺乏现代 io 方法,导致内核在终端上操作时回退到 writev
/readv
的写/读循环.
关于c - list I/O writev 在内部是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9336572/