简体   繁体   English

读取管道块,直到在管道末端运行的程序终止 - Windows

[英]read on a pipe blocks until program running at end of pipe terminates - Windows

I have a sample program that outputs a line of text every second.我有一个示例程序,每秒输出一行文本。 In the test program below, this program writes some text to stdout then waits 1 second and repeats 20 times.在下面的测试程序中,该程序将一些文本写入stdout然后等待 1 秒并重复 20 次。

I have another program which uses popen ( _popen on Windows) to open a pipe for reading from the program.我有另一个程序,它使用popen (Windows 上的_popen )打开管道以从程序中读取。 I then use fgets to read data.然后我使用fgets来读取数据。 The problem I have is that the fgets blocks until the program terminates.fgets的问题是fgets阻塞直到程序终止。 Then I get all the output, all 20 lines, in one go.然后我一次性获得所有输出,所有 20 行。 I want to get the output a line at a time, then ok for fgets to block until next line ready.我想一次输出一行,然后可以让fgets阻塞直到下一行准备好。 The reason is I plan to use this on a program that will be constantly running, outputting text, eg like the use of tail .原因是我计划在一个不断运行的程序上使用它,输出文本,例如使用tail

If I run this code example on a program that outputs some text all in one go and exits then it works fine.如果我在一次输出一些文本并退出的程序上运行此代码示例,则它可以正常工作。

Why does fgets block?为什么fgets阻塞? The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?测试程序会立即打印一些文本,那么为什么fgets不立即读取第一行文本呢?

Here is the code:这是代码:

#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]);
}

The program run, helloworld.exe :程序运行, 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?为什么fgets阻塞?

Because it's waiting for the children to output something.因为它在等待孩子们输出一些东西。

The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?测试程序会立即打印一些文本,那么为什么fgets不立即读取第一行文本呢?

It actually does not print text immediately.它实际上不会立即打印文本。 The problem here, as @Barmar notices, is that writing to a pipe is buffered (and not line buffered) by the C standard library implementation.正如@Barmar 所注意到的,这里的问题是写入管道是由 C 标准库实现缓冲(而不是缓冲)的。 This buffering happens in your child program ( helloworld ), not in your parent program ( pipe_test ).这种缓冲发生在您的子程序 ( helloworld ) 中,而不是在您的父程序 ( pipe_test ) 中。

From your parent program, you have no control over what the children spawned through popen() will do, therefore if the child output is buffered like in this case, the only thing you can do (without modifying the child's code) is to wait until the buffer is flushed to the pipe.从您的父程序中,您无法控制通过popen()生成的子项将执行的操作,因此如果子输出像在这种情况下那样被缓冲,您唯一可以做的事情(不修改子项的代码)就是等到缓冲区被刷新到管道。

In order to get the output sooner, you would have to modify the children's code to manually call fflush() or use setvbuf() to disable buffering:为了更快地获得输出,您必须修改子代的代码以手动调用fflush()或使用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);
    }
}

There's really not much else you can do.你真的没有太多可以做的了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM