setbuf 只影响 stdio 调用,不影响系统调用?

setbuf only affects stdio calls, not system calls?

我试图更深入地了解缓冲在 Cstdio 中的工作原理,我发现了一些我觉得很有趣的东西。我阅读了 this 文章,但我想确认我的理解是否正确。

当我使用 fgets 时,将输入缓冲设置为 3 个字节 (stdbuf -i3) 并检查 strace,我看到读取以 3 个字符为一组进行,这我预计:

read(0, "hel", 3)                       = 3
read(0, "lo\n", 3)                      = 3
...

如果我仍然使用 stdbuf 但这次我改为执行 read(2) 系统调用,它会立即读取全部内容(最多 4096 字节):

read(0, "hello\nworld!\n\n", 4096)      = 14

所以它忽略了 stdbuf 调用。

这让我彻底重新思考 stdio 缓冲。 stdio 本质上是否维护自己的缓冲区,而 stdbuf -i3 是说从内核中的主管道缓冲区一次读入该缓冲区 3 个字节?我认为做 stdbuf -i0 会使管道的容量无法容纳超过一个字节(即 write(2) 调用会在进程发送 stdout 的 1 个字节后阻塞进程的无缓冲 stdin).

那么说在实际管道缓冲区之上有 2 个缓冲区进一步缓冲 stdoutstdin 是否正确?而 setbuf 只控制这两个缓冲区,而不是修改内核管道缓冲区的任何属性。因此,将 stdin 设置为 n 字节缓冲区意味着它将在返回内核以执行 read 系统调用之前保留 n 字节。

缓冲仅存在于 libc 中,即您从 stdio 调用的函数。系统调用接口没有缓冲。如果您进行系统调用(使用例如 readwrite),通常 libc 包装器在调用内核之前会做很少的事情。

documentation for stdbuf

command must start with the name of a program that ... uses the ISO C FILE streams for input/output (note the programs dd and cat don’t do that),

这是因为stdbuf works通过pre-loading一个库进入目标程序从而改变了缓冲模式(在它的libc中)。直接进行系统调用的程序不受影响。

stdbuf 不会改变内核中管道缓冲区的大小。此缓冲区与 libc 提供的缓冲无关。


Does stdio maintain its own buffer essentially, and stdbuf -i3 is saying to read into that buffer 3 bytes at a time from the main pipe buffer in the kernel?

是的。 Libc 为 stdio.h FILE 流提供缓冲区。