在我的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;
}
stdout 进入终端时的预期输出:
$ ./iobuffer
1. output from printf()
2. output from a command called using system()
stdout 重定向到管道或文件时输出乱序:
$ ./iobuffer | cat
2. output from a command called using system()
1. output from printf()
答案 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()
明确刷新,或使用stdout
明确设置setvbuf()
上的缓冲。
答案 1 :(得分:2)
此回复补充了Martijn Pieters的回复。描述流的默认缓冲模式的源代码的引用最后。
以下是三个基本解决方案的解释:
a)在调用子进程之前刷新缓冲区。当您从主程序执行大量输出并且只进行少量子进程调用时,此选项可能会更加有效。
printf("1. output from printf()\n");
fflush(stdout);
system("echo '2. output from a command called using system()'");
b)将整个程序的 stdout 的缓冲更改为行缓冲(或无缓冲)。此选项只是对程序的一个小改动,因为您只在开始时调用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)通过外部实用程序将整个程序的 stdout 的缓冲更改为行缓冲(或无缓冲)。此选项根本不会更改程序,因此您无需重新编译或甚至拥有程序源。您只需使用stdbuf
实用程序调用该程序。
$ stdbuf -oL ./iobuffer | cat
1. output from printf()
2. output from a command called using system()
例如,在下面的文档中描述了初始缓冲设置。默认情况下,对终端等交互式设备的流进行行缓冲,以便在终端上立即显示换行结束消息。管道,文件等使用块缓冲(或完全缓冲)以获得更好的性能。
GNU C Library参考手册
http://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html#Buffering-Concepts
新打开的流通常是完全缓冲的,但有一个例外: 连接到诸如终端的交互设备的流是 最初是行缓冲的。
Linux man-pages:stdin(3)
http://linux.die.net/man/3/stdin
流stderr是无缓冲的。流标准输出是行缓冲的 当它指向一个终端。部分线将不会出现 调用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
最初打开时,标准错误流未完全缓冲; 标准输入和标准输出流是完全缓冲的,如果 并且只有当流可以被确定为不参考时 互动设备。
开放组:系统接口和标题第4版,第2版;
2.4标准I / O流,第32页
https://www2.opengroup.org/ogsys/catalog/C435(需要免费注册下载)
打开时,标准 错误流未完全缓冲;标准输入和标准 当且仅当流可以时,输出流才完全缓冲 决定不参考互动设备。
还有一个非常有趣的章节2.4.1“文件描述符和标准I / O流的交互”关于缓冲和非缓冲I / O的组合,这有点与子进程调用有关。