在APUE(UNIX环境下的高级编程)中,有很多
err_quit
,err_sys
之类的。
例如
err_quit
的来源如下:
void err_quit(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
exit(1);
}
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
char buf[MAXLINE];
vsnprintf(buf, MAXLINE-1, fmt, ap);
if (errnoflag)
snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
strerror(error));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */ /* LINE A*/
fputs(buf, stderr);
fflush(NULL); /* flushes all stdio output streams */ /* LINE B*/
}
我的问题是
LINE A
是必要的,因为LINE B
的存在?我无法理解LINE A
中的评论。
此代码同时打印到
stdout
和 stderr
。当标准输出和标准错误流连接到同一管道或文件时,默认情况下它们是“完全缓冲”的,然后这些流的输出可能会混合。完全缓冲意味着流软件不会立即将输出发送到设备,而是将其内部记录在缓冲区中,并仅在缓冲区已满或请求刷新时才将其发送到设备。这样做是为了提高效率,因为它减少了处理输出所需的系统调用和 I/O 操作的数量。每个流都有一个单独的缓冲区。
例如,假设程序在调用此例程 stdout
之前向
err_doit
写入了一些文本,可能是文本 Continuing program operations, everything is good.\n
,然后调用 err_doit
并将 Error, the deaggregating fluxinator reported a zizzification error.\n
写入两个 stderr
。没有 fflush
呼叫会发生什么:
当第一条消息写入 stdout
Continuing program operations,
填满了缓冲区,因此流软件将完整的缓冲区发送到管道或文件。然后流软件将其余文本 everything is good.\n
err_doit
Error, the deaggregating fluxinator reported a zizzification error.\n
至 stderr
。当程序终止时,缓冲区将被刷新。也许先写入 stderr
Error, the deaggregating fluxinator reported a zizzification error.\n
到 stderr
被发送到管道或文件。然后 stdout
everything is good.\n
发送到管道或文件。
Continuing program operations,Error, the deaggregating fluxinator reported a zizzification error.
everything is good.
因此,用户最终会在管道或文件中看到破碎且混合的消息。那不是我们想要的。为了避免这种情况,程序在写入
stdout
之前先刷新
stderr
,然后再刷新 stderr
。这确保管道或文件将接收:Continuing program operations, everything is good.
Error, the deaggregating fluxinator reported a zizzification error.
当您在命令行运行程序并让标准输出和标准错误连接到终端时,您通常不会看到这一点。这是因为,当这些流连接到终端时,默认情况下它们是“行缓冲”的,这意味着程序将在内部记录输出,并且仅在缓冲区已满时将其发送到设备,换行符是写入,或者请求刷新。因此,每当程序写入包含换行符的行时,直到该字符的输出都会立即发送到终端。只要程序以换行符结束每个输出,就可以防止
stdout
和 stderr
数据交织。