c - list I/O writev 在内部是如何工作的?

标签 c io posix

writev 函数接受一个 struct iovec 数组作为输入参数

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

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

writev 内部是否这样做:

for(iov中的每个元素) 写入(元素)

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

最佳答案

根据标准,您提到的 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

回答问题的最后一部分:

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/

相关文章:

c - 如何在关闭 TCP 连接之前发送消息 [POSIX]

C 调查程序

c - 防止包含 stdio.h(或其他标准头文件)

Python - tempfile.TemporaryFile 无法读取;为什么?

C - I/O - 用于打印句子结构的字符数组

c++ - C POSIX 函数的 header

在数组中生成素数时出现 C 段错误

c - scanf() 和 for 循环不合作

scala - 设计从 channel 读取的引用透明函数

有人可以解释 fork 是如何工作的吗?