读取管道块,直到管道末端的程序 运行 终止 - Windows
read on a pipe blocks until program running at end of pipe terminates - Windows
我有一个每秒输出一行文本的示例程序。在下面的测试程序中,该程序将一些文本写入 stdout
然后等待 1 秒并重复 20 次。
我有另一个程序,它使用 popen
(Windows 上的 _popen
)打开管道以从程序读取。然后我使用 fgets
来读取数据。我遇到的问题是 fgets
阻塞,直到程序终止。然后我一次性获得所有输出,全部 20 行。我想一次输出一行,然后确定 fgets
阻塞直到下一行准备好。原因是我计划在一个程序上使用它,该程序将不断 运行ning,输出文本,例如比如使用 tail
.
如果我 运行 程序中的此代码示例一次性输出一些文本并退出,那么它工作正常。
为什么 fgets
会阻塞?测试程序确实会立即打印一些文本,那么为什么 fgets
不立即读取第一行文本?
代码如下:
#include <stdio.h>
#include <windows.h>
void execute(const char* cmd) {
char buffer[128] = { 0 };
FILE* pipe = _popen(cmd, "r");
if (!pipe) {
printf("popen() failed!\n");
return;
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != nullptr)
printf("%s", buffer);
}
int rc = _pclose(pipe);
if (rc != EXIT_SUCCESS) { // return code not 0
printf("pclose exit failure: %d\n", rc);
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: pipe_test.exe <program>\n");
exit(1);
}
execute(argv[1]);
}
程序运行,helloworld.exe
:
#include <stdio.h>
#include <windows.h>
int main() {
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
Why does fgets
block?
因为它在等待 children 输出一些东西。
The test program does print some text immediately, so why doesn't fgets
read this first line of text immediately?
它实际上不会立即打印文本。正如 @Barmar 所指出的,这里的问题是写入管道是由 C 标准库实现缓冲的(而不是 line 缓冲的)。这种缓冲发生在您的 child 程序 (helloworld
) 中,而不是在您的 parent 程序 (pipe_test
) 中。
在你的 parent 程序中,你无法控制通过 popen()
产生的 children 会做什么,因此如果 child 输出像在这种情况下,您唯一能做的(不修改 child 的代码)就是等待缓冲区被刷新到管道。
为了更快地获得输出,您必须修改 children 的代码以手动调用 fflush()
or use setvbuf()
以禁用缓冲:
int main() {
setvbuf(stdout, NULL, _IONBF, 0); // Disable buffering on stdout.
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
您真的无能为力。
我有一个每秒输出一行文本的示例程序。在下面的测试程序中,该程序将一些文本写入 stdout
然后等待 1 秒并重复 20 次。
我有另一个程序,它使用 popen
(Windows 上的 _popen
)打开管道以从程序读取。然后我使用 fgets
来读取数据。我遇到的问题是 fgets
阻塞,直到程序终止。然后我一次性获得所有输出,全部 20 行。我想一次输出一行,然后确定 fgets
阻塞直到下一行准备好。原因是我计划在一个程序上使用它,该程序将不断 运行ning,输出文本,例如比如使用 tail
.
如果我 运行 程序中的此代码示例一次性输出一些文本并退出,那么它工作正常。
为什么 fgets
会阻塞?测试程序确实会立即打印一些文本,那么为什么 fgets
不立即读取第一行文本?
代码如下:
#include <stdio.h>
#include <windows.h>
void execute(const char* cmd) {
char buffer[128] = { 0 };
FILE* pipe = _popen(cmd, "r");
if (!pipe) {
printf("popen() failed!\n");
return;
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != nullptr)
printf("%s", buffer);
}
int rc = _pclose(pipe);
if (rc != EXIT_SUCCESS) { // return code not 0
printf("pclose exit failure: %d\n", rc);
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: pipe_test.exe <program>\n");
exit(1);
}
execute(argv[1]);
}
程序运行,helloworld.exe
:
#include <stdio.h>
#include <windows.h>
int main() {
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
Why does
fgets
block?
因为它在等待 children 输出一些东西。
The test program does print some text immediately, so why doesn't
fgets
read this first line of text immediately?
它实际上不会立即打印文本。正如 @Barmar 所指出的,这里的问题是写入管道是由 C 标准库实现缓冲的(而不是 line 缓冲的)。这种缓冲发生在您的 child 程序 (helloworld
) 中,而不是在您的 parent 程序 (pipe_test
) 中。
在你的 parent 程序中,你无法控制通过 popen()
产生的 children 会做什么,因此如果 child 输出像在这种情况下,您唯一能做的(不修改 child 的代码)就是等待缓冲区被刷新到管道。
为了更快地获得输出,您必须修改 children 的代码以手动调用 fflush()
or use setvbuf()
以禁用缓冲:
int main() {
setvbuf(stdout, NULL, _IONBF, 0); // Disable buffering on stdout.
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
您真的无能为力。