Необходимо ли использовать fflush более одного раза?

В APUE (Расширенное программирование в среде UNIX) есть множество err_quit, err_sys и им подобных.

Источник, например, err_quit следующий:

void err_quit(const char *fmt, ...) {
    va_list     ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
    char    buf[MAXLINE];

    vsnprintf(buf, MAXLINE-1, fmt, ap);
    if (errnoflag)
        snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
          strerror(error));
    strcat(buf, "\n");
    fflush(stdout);     /* in case stdout and stderr are the same */  /* LINE A*/
    fputs(buf, stderr);
    fflush(NULL);       /* flushes all stdio output streams */        /* LINE B*/
}

Мой вопрос LINE A необходим из-за существования LINE B ? Не могу понять комментарии вLINE A.

Ваша единственная запись в stderr с помощью fputs (buf, "\n"); (что само по себе является неправильным использованием ` int fputs(const char *s, FILE *stream);` ) "\n" не представляет собой объект FILE *stream. Теперь сделайте шаг назад и спросите: «Почему я думаю, что мне нужно fflush(stdout);? stdout по умолчанию буферизуется по строкам, поэтому каждый раз, когда ваш вывод включает в себя символ '\n', эта строка будет записана в stdout без необходимости вызова fflush(stdout);. Теперь, если вы просто fputs ("xxxx", stdout); где нет '\n' в конце "xxxx" - тогда вам нужно будет fflush(stdout); принудительно вывести "xxxx" из буфера раньше.

David C. Rankin 03.04.2024 11:08

@DavidC.Rankin Я не вижу fputs (buf, "\n") нигде в коде ОП, который на самом деле является кодом У. Рича Стивенса.

user207421 03.04.2024 11:16

См. Почему printf не сбрасывается после вызова, если в строке формата нет новой строки? для более подробного обсуждения темы. @user207421 user207421, это было плохо fputs (buf, stderr); -- старею...

David C. Rankin 03.04.2024 11:16

Идея состоит в том, чтобы сбросить stdout перед stderr, чтобы их ожидающие выходные данные не перекрывались. Так что «более одного раза» на самом деле не применимо.

user207421 03.04.2024 11:17

@DavidC.Rankin: stdout не имеет «строчной буферизации по умолчанию». Это зависит от того, с чем оно связано. Итак, чтобы во всех случаях получить желаемое поведение, можно использовать fflush, как указано выше для пользователя 207421.

Eric Postpischil 03.04.2024 11:19

Пока вы не перенаправляете его, он буферизуется по строкам.

David C. Rankin 03.04.2024 11:20

@DavidC.Rankin: Re: «Пока вы не перенаправляете его, он буферизуется по строкам». Так? Это означает, что существуют ситуации, когда stdout не буферизуется по строкам, поэтому для получения желаемого поведения необходимо использовать fflush.

Eric Postpischil 03.04.2024 11:23

Извините, я не увидел в Вопросе ни одного подходящего случая. На какой текст вы там опираетесь?

David C. Rankin 03.04.2024 13:12

@DavidC.Rankin Дело в том, что это очень защитный код, который не предполагает, что stderr и stdout различны или что stdout не было перенаправлено.

user207421 03.04.2024 13:19

@DavidC.Rankin: Какой угловой случай? Это простой основной случай. Если вы направляете и стандартный вывод, и стандартную ошибку в один и тот же канал или файл, они полностью буферизуются, и вывод может чередоваться. Когда программа записывает в stdout, часть данных будет отправлена ​​в канал или файл, когда буфер заполнен, а затем часть будет удерживаться в буфере. Затем, когда программа записывает в stderr, это можно отправить в канал или файл, пока в буфере stdout еще есть ожидающие данные, так что вы можете фрагменты сообщений из одного потока переплетать с данными из другого. Промывка позволяет избежать этого.

Eric Postpischil 03.04.2024 15:14

@tahzibi.jafar, Кроме того: вместо strlen(), strcat() используйте возвращаемое значение vsnprintf() (если не отрицательное), чтобы быстро определить длину буфера.

chux - Reinstate Monica 03.04.2024 16:15

@tahzibi.jafar, Комментарий в fflush(NULL); /* flushes all stdio output streams */ неуместен. fflush(NULL); очищает все выходные потоки, а не только все выходные потоки stdio. Сюда входят потоки вывода, связанные с файлами и т. д. Так что лучше как fflush(NULL); /* flush all output streams */ .

chux - Reinstate Monica 03.04.2024 16:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Эта программа, по-видимому, печатает как stdout, так и stderr. В Linux стандартный поток ошибок по умолчанию не буферизуется. Когда стандартный поток вывода подключен к каналу или файлу, он по умолчанию полностью буферизуется, и тогда вывод в эти потоки может стать смешанным. Небуферизованный означает, что программное обеспечение потоков отправляет выходные данные на устройство, как только программа их записывает. Полная буферизация означает, что программное обеспечение потока не отправляет выходные данные на устройство немедленно, а вместо этого записывает их внутри себя в буфер и отправляет на устройство только тогда, когда буфер заполнен или запрашивается очистка. Это сделано для повышения эффективности, поскольку уменьшает количество системных вызовов и операций ввода-вывода, необходимых для обработки выходных данных. Каждый буферизованный поток имеет отдельный буфер.

Например, предположим, что программа записала некоторый текст в stdout до вызова этой подпрограммы err_doit, возможно, текст Continuing program operations, everything is good.\n, а затем вызывается err_doit и записывает Error, the deaggregating fluxinator reported a zizzification error.\n в обе stderr. Что может произойти без вызовов fflush:

  • Когда первое сообщение было записано в stdout, Continuing program operations, заполнил буфер, поэтому программное обеспечение потока отправило полный буфер в канал или файл.
  • Затем программное обеспечение потока помещает остальной текст everything is good.\n в буфер.
  • err_doit позвонили и написали Error, the deaggregating fluxinator reported a zizzification error.\n на stderr. Поскольку стандартная ошибка не буферизована, она была отправлена ​​немедленно.
  • Когда программа завершается, буферы очищаются. Когда stdout сбрасывается, everything is good.\n отправляется в канал или файл.

В результате канал или файл получает следующий текст:

Continuing program operations,Error, the deaggregating fluxinator reported a zizzification error.
 everything is good.

Таким образом, пользователь в конечном итоге видит разрезанные и смешанные сообщения в канале или файле. Это не то, чего мы хотим. Чтобы избежать этого, программа сбрасывает stdout перед записью в stderr. Это гарантирует, что канал или файл получит:

Continuing program operations, everything is good.
Error, the deaggregating fluxinator reported a zizzification error.

Обычно вы этого не увидите, если запустите программу из командной строки и позволите стандартному выводу и стандартной ошибке подключиться к терминалу. Это связано с тем, что когда стандартный вывод подключен к терминалу, по умолчанию он буферизуется по строкам, то есть программа будет записывать вывод внутри себя и отправлять его на устройство только тогда, когда буфер заполнен, записывается символ новой строки, или требуется промывка. Таким образом, всякий раз, когда программа записывает строку, содержащую символ новой строки, вывод до этого символа немедленно отправляется на терминал. Это предотвращает переплетение данных stdout и stderr, пока программа заканчивает каждый вывод символом новой строки.

Это объясняет первое fflush(stdout);. Причина последнего fflush(NULL); (который запрашивает очистку всех потоков, возможно, ожидающих вывода) не ясна. Он очистит стандартный поток ошибок, если он буферизован, но это может быть намерением автора, а может и не быть.

Другие вопросы по теме