【问题标题】:stderr and stdout - not buffered vs. buffered?标准错误和标准输出 - 没有缓冲与缓冲?
【发布时间】:2013-11-28 05:26:52
【问题描述】:

我正在编写一个小程序,它根据不同的事件具有各种控制台输出字符串。当我在寻找发送这些消息的最佳方式时,我遇到了一些令人困惑的事情。

我读到stderr 用于将消息直接发送到控制台 - not buffered。而相比之下,我读到stdoutbuffered 并且通常用于将消息重定向到各种streams?,这可能是也可能不是错误消息,输出文件或其他介质。

说某事被缓冲和不被缓冲有什么区别?当我读到消息被直接发送到输出并且缓冲时,这是有道理的。但同时我意识到我并不完全确定缓冲的含义。

【问题讨论】:

    标签: c stream stdout stderr


    【解决方案1】:

    假设

    int main(void)
    {
        printf("foo\n");
        sleep(10);
        printf("bar\n");
    }
    

    在控制台执行时

    $ ./a.out
    

    您将看到foo 行和10 秒后bar 行(--> 行缓冲)。将输出重定向到文件或管道时

    $ ./a.out > /tmp/file
    

    文件保持为空(--> 缓冲)直到程序终止(--> 退出时隐式 fflush())。

    如果上面的行不包含\n,在程序终止之前,您也不会在控制台上看到任何内容。

    在内部,printf() 将一个字符添加到缓冲区。为了让事情更容易,让我描述fputs(char const *s, FILE *f) 而不是。 FILE 可能被定义为

    struct FILE {
        int fd;   /* is 0 for stdin, 1 for stdout, 2 for stderr (usually) */
        enum buffer_mode mode;
        char buf[4096];
        size_t count; /* number of chars in buf[] */
    };
    typedef struct FILE *FILE;
    
    int fflush(FILE *f)
    {
        write(f->fd, f->buf, f->count);
        f->count = 0;
    }
    
    int fputc(int c, FILE *f)
    {
        if (f->count >= ARRAY_SIZE(f->buf))
            fflush(f);
        f->buf[f->count++] = c;
    } 
    
    int fputs(char const *s, FILE *f)
    {
        while (*s) {
            char c = *s++;
    
            fputc(c, f);
    
            if (f->mode == LINE_BUFFERED && c == '\n')
                fflush(f);
        }
    
        if (f->mode == UNBUFFERED)
            fflush(f);
    }
    

    【讨论】:

    • 我确实读到所有输出流在退出时都被刷新,所以这是有道理的。但是为什么要缓冲文件的输出,直到程序退出才开始呢?如果要写入文件的输出非常长怎么办?
    • printf() 使用一定大小的内部缓冲区(例如 4KiB)。当此缓冲区已满时,fflush() 将被隐式调用并出现输出。
    • 直觉上说有道理,但也有点令人困惑。如果您要使用类似(单个语句)printf("printing") 的东西,会在函数返回之前立即调用 fflush() 吗?当printf() 的缓冲区完全填满并隐式刷新时,什么时候会出现这种情况?是不是有很多重复调用printf()函数的时候?
    • 感谢您的代码。现在更有意义了。不过,我对你在最后一个函数 fputs 中所做的事情有点困惑。我一直跟着你,直到你用*s 来做一个布尔值。这段代码及其后面的代码的目的是什么?
    【解决方案2】:

    当输出流被缓冲时,这意味着流不一定会在您告诉它的那一刻输出数据。每个 IO 操作可能会有很大的开销,因此大量的小 IO 操作会造成瓶颈。通过缓冲 IO 操作,然后一次刷新许多操作,可以减少这种开销。

    虽然stdoutstderr 在缓冲方面的表现可能不同,但这通常不是它们之间的决定因素,不应依赖。如果您绝对需要立即输出,请始终手动flush 流。

    【讨论】:

    • 你指的是刷新标准输出吗?使用任一流之间的决定因素是什么?
    • stdout 用于“输出”,stderr 用于“错误”。这里很好地解释了何时使用每个jstorimer.com/blogs/workingwithcode/…
    【解决方案3】:

    缓冲流是您一直写入到某个阈值的流。该阈值可以是特定字符,如 Konrad 提到的行缓冲,也可以是其他阈值,例如写入的特定字符数。

    缓冲旨在加速输入/输出操作。计算机做的最慢的事情之一就是写入流(无论是控制台还是文件)。当不需要立即看到的东西时,将其存储一段时间可以节省时间。

    你是对的,stderr 通常是无缓冲的流,而stdout 通常是缓冲的。所以有时你输出东西到stdout 然后输出到stderrstderr 首先出现在控制台上。如果您想让stdout 的行为相似,则必须在每次写入后flush

    【讨论】:

      【解决方案4】:

      通常,stdout 是行缓冲的,这意味着发送到 stdout 的字符“堆叠”直到换行符到达,此时全部输出。

      【讨论】:

      • 啊,所以要打印到屏幕的字符被缓冲到换行符为止?如果在收到第一个字符后,不存在第二个new line 字符怎么办?缓冲的输出会一次性复制到控制台吗?
      • 正如一些人已经指出的那样,换行并不是唯一会导致缓冲输出被“刷新”的东西。另一个潜在原因是字符数达到一定阈值或用户调用fflush()
      猜你喜欢
      • 1970-01-01
      • 2013-10-27
      • 1970-01-01
      • 2014-12-07
      • 2016-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多