Понимание необходимости fflush() и связанных с ним проблем
Ниже приведен пример кода для вызов fflush():
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <io.h>
void flush(FILE *stream);
int main(void)
{
FILE *stream;
char msg[] = "This is a test";
/* create a file */
stream = fopen("DUMMY.FIL", "w");
/* write some data to the file */
fwrite(msg, strlen(msg), 1, stream);
clrscr();
printf("Press any key to flush DUMMY.FIL:");
getch();
/* flush the data to DUMMY.FIL without closing it */
flush(stream);
printf("nFile was flushed, Press any key to quit:");
getch();
return 0;
}
void flush(FILE *stream)
{
int duphandle;
/* flush the stream's internal buffer */
fflush(stream);
/* make a duplicate file handle */
duphandle = dup(fileno(stream));
/* close the duplicate handle to flush the DOS buffer */
close(duphandle);
}
все, что я знаю о fflush (), это то, что это библиотечная функция, используемая для очистки выходного буфера. Я хочу знать, какова основная цель использования fflush () и где я могу ее использовать. И главным образом мне интересно знать какие проблемы могут быть с вызов fflush().
3 ответов
немного сложно сказать, с чем " могут быть проблемы "(чрезмерно?) использование fflush
. Все виды вещей can быть, или стать, проблемы, в зависимости от ваших целей и подходов. Вероятно, лучший способ взглянуть на это-то, что намерение fflush
есть.
первое, что нужно учитывать, это fflush
определяется только в выходных потоках. Выходной поток собирает "вещи для записи в файл" в большой (ish) буфер, а затем записывает этот буфер в файл. Смысл этого сбора-и-написания-позже заключается в повышении скорости / эффективности двумя способами:
- в современных ОС существует некоторое наказание за пересечение границы защиты пользователя / ядра (система должна изменить некоторую информацию о защите в ЦП и т. д.). Если вы делаете большое количество вызовов записи уровня ОС, вы платите этот штраф за каждый из них. Если вы соберете, скажем, 8192 или около того, отдельные записи в один большой буфер, а затем сделаете один вызов, вы удалите большую часть этого накладные расходы.
- на многих современных ОС каждый вызов записи ОС будет пытаться каким-то образом оптимизировать производительность файла, например, обнаружив, что вы расширили короткий файл на более длинный, и было бы хорошо переместить дисковый блок из точки А на диске в точку Б на диске, чтобы более длинные данные могли поместиться смежно. (В старых Осах это отдельный шаг "дефрагментации", который вы можете запустить вручную. Вы можете думать об этом как о современной ОС, выполняющей динамические, мгновенные дефрагментация.) Если вы напишете, скажем, 500 байт, а затем еще 200, а затем 700 и так далее, это будет делать много этой работы; но если вы сделаете один большой вызов, скажем, с 8192 байтами, ОС может выделить большой блок один раз, и поместить все туда и не придется повторно дефрагментировать позже.
Итак, люди, которые предоставляют вашу библиотеку C и ее реализацию потока stdio, делают все возможное в вашей ОС, чтобы найти "разумно оптимальный" размер блока и собрать все выход в кусок такого размера. (Числа 4096, 8192, 16384 и 65536 часто, сегодня, как правило, хорошие, но это действительно зависит от ОС, а иногда и от базовой файловой системы. Обратите внимание, что" больше "не всегда" лучше": потоковая передача данных в кусках по четыре гигабайта за раз, вероятно, будет работать хуже, чем делать это в кусках 64 Кбайт, например.)
но это создает проблему. Предположим, вы записываете в файл, например файл журнала со штампами даты и времени и сообщения, и ваш код будет продолжать писать в этот файл позже, но прямо сейчас, он хочет приостановить на некоторое время и давайте лог-анализатор прочитать текущее содержимое файла журнала. Один из вариантов-использовать fclose
закрыть файл журнала, а затем fopen
открыть его снова, чтобы добавить больше данных. Более эффективно, однако, толкать любые ожидающие сообщения журнала в базовый файл ОС, но держать файл открытым. Вот что!--0--> делает.
буферизация также создает другой проблема. Предположим, ваш код имеет какую-то ошибку, и он иногда падает, но вы не уверены, что он вот-вот рухнет. И предположим, вы что-то написали, и это очень важно!--20-->этой данные выходят в базовую файловую систему. Вы можете позвонить fflush
чтобы протолкнуть данные в ОС, прежде чем вызывать потенциально плохой код, который может произойти сбой. (Иногда это хорошо для отладки.)
или, предположим, вы находитесь на Unix-подобной системе, и fork
системный вызов. Этот вызов дублирует все пользовательское пространство (делает клон исходного процесса). Буферы stdio находятся в пользовательском пространстве, поэтому клон имеет те же буферизованные, но еще не записанные данные, что и исходный процесс во время fork
звонок. Здесь снова один из способов решить проблему-использовать fflush
чтобы вытолкнуть буферизованные данные непосредственно перед выполнением fork
. Если все выйдет до fork
, нет ничего дублировать; свежий клон никогда не будет пытаться написать буферизованные данные, поскольку они больше не существуют.
больше fflush
-es вы добавляете, тем больше вы побеждаете оригинальную идею сбора больших кусков данных. То есть вы делаете компромисс: большие куски более эффективны, но вызывают какую-то другую проблему, поэтому вы принимаете решение: "будьте менее эффективны здесь, чтобы решить проблему, более важную, чем просто эффективность". Вы звоните fflush
.
Иногда проблема заключается просто в "отладке программного обеспечения". В таком случае, вместо многократного вызова fflush
, вы можете использовать такие функции, как setbuf
и setvbuf
чтобы изменить поведение буферизации потока stdio. Это удобнее (требуется меньше или даже нет изменений кода-Вы можете управлять вызовом set-buffering с флагом), чем добавлять много fflush
вызовы, так что можно считать " проблемой с использованием (или чрезмерным использованием)fflush
".
ну, ответ @torek почти идеален, но есть один момент, который не так точен.
первое, что нужно учитывать, это то, что fflush определяется только на выходе потоки.
согласно человеку fflush, fflush можно также использовать в вход потоки:
для выходных потоков fflush () заставляет записывать все пользовательское пространство буферизованные данные для данного потока вывода или обновления через поток основной функция write. для входные потоки, fflush () отбрасывает любые буферизованные данные, которые были извлечены из базового файла, но не были потреблены приложение. Открытый статус поток не затронут. Таким образом, при использовании input, fflush просто отбрасывает его.
вот демонстрация, чтобы проиллюстрировать это:
#include<stdio.h>
#define MAXLINE 1024
int main(void) {
char buf[MAXLINE];
printf("prompt: ");
while (fgets(buf, MAXLINE, stdin) != NULL)
fflush(stdin);
if (fputs(buf, stdout) == EOF)
printf("output err");
exit(0);
}
fflush()
очищает буферы, связанные с потоком. если вы, например, позволяете пользователю вводить некоторые данные в очень короткий промежуток времени (миллисекунды) и записывать некоторые вещи в файл, буферы записи и чтения могут иметь некоторый "reststuff", остающийся в себе. вы звоните fflush()
затем, чтобы очистить все буферы и заставить стандартные выходы убедиться, что следующий вход вы получите то, что пользователь нажал тогда.