2013-07-03 35 views
2

在我的C程序中,在Linux上运行,它使用system()创建子进程,我注意到当我将stdout重定向到管道或文件时,子进程的输出在缓冲区输出之前发送I/O功能,如printf()。当stdout被留下去终端,然后输出按预期的顺序。我简化了程序,以下面的例子:为什么程序输出重定向会导致其子进程的输出出现故障?

#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    printf("1. output from printf()\n"); 
    system("echo '2. output from a command called using system()'"); 

    return EXIT_SUCCESS; 
} 

标准输出前进到终端的预期输出:

$ ./iobuffer 
1. output from printf() 
2. output from a command called using system() 

输出失灵时标准输出被重定向到一个管道或文件:

$ ./iobuffer | cat 
2. output from a command called using system() 
1. output from printf() 
+0

可能的重复[为什么stdout需要显式刷新时重定向到文件?](http://stackoverflow.com/questions/13932932/why-does-stdout-need-explicit-flushing-when-redirected-to-文件) –

+0

不幸的是我没有找到类似的问题[为什么标准输出重定向到文件时需要显式刷新?](http://stackoverflow.com/questions/13932932/why-does-stdout-need-explicit-flushing-when重定向到文件)虽然我尝试了多次搜索。我最近遇到了这个问题,当我写这个问题时,我已经知道了答案。我认为这里缺少这个问题。可能它仍然有用,因为我的搜索没有工作:)我准备了一个答案,但我不知道我不会立即被允许回答我的问题:( – pabouk

+0

“提问”页面上有一个复选框,也可以打开一个答案文本框,然后您可以一次性发布问题*和*答案,如果您发布*只是一个问题,您必须稍等一下。 –

回答

4

终端通常使用线路缓冲,而管道将使用块缓冲。

这意味着您的printf调用(包含换行符)将填充行缓冲区,从而触发刷新。重定向时,程序完成前不会进行刷新。

echo另一方面,当它正在写入的缓冲区时,它总是写入到完成。

随着线缓冲(端子输出),则顺序为:

  • printf()打印带有换行的线,缓冲器被刷新,可以看到被印刷1. output from printf()
  • echo写输出,退出,刷新缓冲区,你看到2. output from a command called using system()打印。

随着块缓冲,顺序为:

  • printf()打印带有换行的线,不完全填充所述缓冲器。
  • echo写输出,退出,刷新缓冲区,你看到2. output from a command called using system()打印。
  • 程序退出,刷新其块缓冲区,您将看到正在打印的1. output from printf()

您的选项是用来刷新明确使用fflush()或与setvbuf()明确设置缓冲上stdout

+0

感谢您令人难以置信的快速回答。事实上我已经知道了答案,但是我不能立即添加,是否值得添加完整的答案或仅仅包含答案中未提及的信息? - 答案:有一个小错误 - 行缓冲只能用'setvbuf()'而不是'setbuf()'来设置,另外我不明白'fprintf()'可以绕过缓冲区,AFAIK使用与'printf()'完全相同的缓冲区。 – pabouk

+0

删除了'fprintf()'声明,我误解了另一条信息,我纠正了'setvbuf()'调用(打字错误)。您始终可以自由添加自己的答案,并让未来的访问者决定投票哪些信息是有用的。 –

2

此回复是对Martijn Pieters的回复的补充。描述流的默认缓冲模式的源的引用最后。

解决方案

这里有与解释三种基本方案:

一)调用子进程前刷新缓冲区。当你从主程序中输出大量的输出并且只有很少的子进程调用时,这个选项可能会更加有效。

printf("1. output from printf()\n"); 
fflush(stdout); 
system("echo '2. output from a command called using system()'"); 

b)中变化缓冲标准输出排队缓冲(或无缓冲),用于整个节目。此选项只是对程序的一个小改动,因为您只在开始时调用sevbuf(),并且程序的其余部分保持不变。

if(setvbuf(stdin, NULL, _IOLBF, BUFSIZ)) 
    err(EXIT_FAILURE, NULL); 
printf("1. output from printf()\n"); 
system("echo '2. output from a command called using system()'"); 

编辑:
c)中变化缓冲标准输出由外部工具来LINE-缓冲(或无缓冲),用于整个节目。这个选项根本不会改变程序,所以你不需要重新编译,甚至不需要程序的源代码。您只需使用stdbuf实用程序调用该程序。

$ stdbuf -oL ./iobuffer | cat 
1. output from printf() 
2. output from a command called using system() 

参考 - 描述为什么缓冲模式改变

的初始缓冲设置在下面的文献中描述的。流到终端等交互设备的流默认是行缓冲的,这样新行结束的消息立即出现在终端上。管道,文件等使用块缓冲(或全缓冲)以获得更好的性能。

GNU C库参考手册
http://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html#Buffering-Concepts

新打开的流通常是完全缓冲,但有一个例外: 连接到交互设备,诸如终端流是 最初行缓冲。

Linux的人的页面:标准输入(3)
http://linux.die.net/man/3/stdin

流标准错误是无缓冲。流标准输出在指向终端时是行缓冲的 。直到 fflush(3)或exit(3)被调用,或者打印换行符时,部分行才会出现。这可能会产生意想不到的结果,尤其是在调试输出时。标准流(或任何其他流)的缓冲模式可以是 ,使用setbuf(3)或setvbuf(3)调用进行更改。

还提到了终端驱动程序的缓冲区。

ISO/IEC 9899:201x C11委员会草案 - 2011年4月12日; 7.21.3文件,第301 ​​
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

由于刚打开,标准错误流尚未完全缓冲; 如果 和只有当流可以确定不参考 交互式设备时,标准输入和标准输出流完全缓冲。

The Open Group:System Interfaces and Headers Issue 4,Version 2; 2.4标准I/O流中,第32页
https://www2.opengroup.org/ogsys/catalog/C435(需要下载免费注册)

在打开时,标准 错误流不完全缓冲;标准输入和标准 输出流完全缓冲当且仅当流可以是 确定不是指交互式设备。

还有一个非常有趣的章节2.4.1“文件描述符和标准I/O流的互动”关于缓冲和无缓冲I/O这在一定程度涉及到子进程调用的结合。

相关问题