Unix C неожиданное поведение каналов с scanf () - PullRequest
2 голосов
/ 23 апреля 2019

Прошло много времени с тех пор, как я последний раз программировал на C, и у меня возникают проблемы с работой каналов. (Для ясности я использую Cygwin в Windows 7.) В частности, мне нужна помощь в понимании поведения следующего примера:

/* test.c */

#include <stdio.h>
#include <unistd.h>


int main() {

    char c;
    //scanf("%c", &c); // this is problematic

    int p[2];
    pipe(p);

    int out = dup(STDOUT_FILENO);

    // from now on, implicitly read from and write on pipe
    dup2(p[0], STDIN_FILENO);
    dup2(p[1], STDOUT_FILENO);

    printf("hello");
    fflush(stdout);

    // restore stdout
    dup2(out, STDOUT_FILENO);
    // should read from pipe and write on stdout
    putchar(getchar());
    putchar(getchar());
    putchar(getchar());
}

Если я призываю:

echo abcde | ./test.exe

Я получаю следующий вывод:

hel

Однако, если я раскомментирую вызов scanf, я получу:

bcd

Что я не могу объяснить. На самом деле это очень упрощенная версия более сложной программы со структурой fork/exec, которая начала вести себя очень плохо. Несмотря на отсутствие циклов, он начал порождать бесконечных детей в бесконечном цикле. Так что, если позволяют правила, мне, вероятно, придется расширить вопрос с более конкретным случаем использования. Большое спасибо.

1 Ответ

4 голосов
/ 23 апреля 2019

Функции потокового ввода-вывода, такие как scanf, обычно выполняют буферизацию для повышения производительности.Таким образом, если вы вызовете scanf на стандартном вводе, то он, вероятно, будет читать больше символов, чем необходимо для удовлетворения запроса, и дополнительный будет ждать, буферизованный, для следующего чтения.

ЗаменаБазовый дескриптор файла не влияет на ранее буферизованные данные.Когда вы впоследствии снова прочитаете файл, вы получите данные, буферизованные в первый раз, пока они не будут исчерпаны, и только тогда вы получите свежие данные из нового базового файла.

Если вы хотите, вы можете отключить буферизациюпоток через функцию setvbuf() перед выполнением каких-либо операций ввода-вывода:

int result = setvbuf(stdin, NULL, _IONBF, 0);
if (result != 0) {
    // handle error ...
}

На самом деле это очень упрощенная версия более сложной программы с использованием fork / execструктура, которая начала вести себя очень плохо.Несмотря на отсутствие циклов, он каким-то образом начал порождать бесконечных детей в бесконечном цикле.

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

Итак, если позволяют правила, мне, вероятно, придется расширить вопрос более конкретным случаем использования.

Это был бы отдельный вопрос.

...