【问题标题】:setbuf only affects stdio calls, not system calls?setbuf 只影响stdio调用,不影响系统调用?
【发布时间】:2019-07-16 02:14:56
【问题描述】:

我正在尝试更深入地了解 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 发送到进程的无缓冲stdin 的进程的1 个字节后阻塞) .

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

【问题讨论】:

    标签: c pipe stdio


    【解决方案1】:

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

    documentation for stdbuf

    command 必须以程序的名称开头...使用 ISO C 文件流进行输入/输出(注意程序 ddcat 不这样做),

    这是因为stdbuf works 通过将库预加载到目标程序中来改变缓冲模式(在其 libc 中)。直接进行系统调用的程序不受影响。

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


    stdio 本质上是否维护自己的缓冲区,并且 stdbuf -i3 表示每次从内核中的主管道缓冲区读取该缓冲区 3 个字节?

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

    【讨论】:

    • 谢谢!那么说stdin指向libc缓冲区(流向管道缓冲区)是否正确,本质上,STDIN_FILENO(或fd 0)直接指向管道缓冲区的写入端,绕过libc 缓冲区?
    • 内核可能并且确实有自己的缓冲区!
    • @AnttiHaapala 这意味着在进行系统调用时我不能保证无缓冲的读取或写入?从我读到的内容看来,STDIN 和 STDIN_FILENO 之间的一个很大区别是前者是缓冲的,而后者不是。但也许他们只是意味着前者后来被stdio缓冲了?
    • 是的...例如,内核总是有用于管道和终端的缓冲区。即使你在没有参数的情况下运行像cat 这样的程序,它无法接收从终端写入的内容,直到换行符或 ctrl+d 因为内核缓冲... 而管道有一个 32-内核内部128K缓冲区
    • @AnttiHaapala 谢谢!所以澄清一下,这意味着当我进行写入系统调用时,数据可能不会立即在管道的读取端可用?如果确实如此,如何使程序中的所有写入都立即可供管道的读取端使用?
    猜你喜欢
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    相关资源
    最近更新 更多