читать на блоках канала до тех пор, пока не завершится работа программы в конце канала - Windows - PullRequest
3 голосов
/ 20 января 2020

У меня есть пример программы, которая выводит строку текста каждую секунду. В тестовой программе ниже эта программа записывает некоторый текст в stdout, затем ждет 1 секунду и повторяет 20 раз.

У меня есть другая программа, которая использует popen (_popen на Windows) для открытия труба для чтения из программы. Затем я использую fgets для чтения данных. У меня проблема в том, что fgets блокируется до завершения программы. Затем я получаю все выходные данные, все 20 строк в одном go. Я хочу получить вывод по одной строке за раз, тогда нормально для fgets, чтобы заблокировать, пока не будет готова следующая строка. Причина в том, что я планирую использовать это в программе, которая будет постоянно работать с выводом текста, например, с использованием tail.

Если я запускаю этот пример кода для программы, которая выводит весь текст в один go и выходит, тогда он работает нормально.

Почему 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);
    }
}

1 Ответ

1 голос
/ 20 января 2020

Почему fgets блокируется?

Поскольку дети ждут, чтобы что-то вывести.

Тестовая программа печатает некоторый текст немедленно, так почему же fgets не читает эту первую строку текста сразу?

На самом деле не печатает текст сразу. Проблема здесь, как замечает @ Barmar , заключается в том, что запись в канал буферизуется (а не line buffered) реализацией стандартной библиотеки C. Эта буферизация происходит в вашей дочерней программе (helloworld), а не в вашей родительской программе (pipe_test).

Из вашей родительской программы вы не можете контролировать, что будут делать дети, порожденные через popen() поэтому, если дочерний вывод буферизуется, как в этом случае, единственное, что вы можете сделать (без изменения дочернего кода), это дождаться, пока буфер не будет сброшен в канал.

Чтобы получить вывод раньше, вам придется изменить код детей, чтобы вручную вызывать 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);
    }
}

Больше ничего не поделаешь.

...