Verstehen der Notwendigkeit von fflush() und der damit verbundenen Probleme

Lesezeit: 8 Minuten

Benutzer-Avatar
Karan

Unten ist Beispielcode für die Verwendung von 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);
}

Alles, was ich über fflush() weiß, ist, dass es sich um eine Bibliotheksfunktion handelt, die zum Leeren eines Ausgabepuffers verwendet wird. Ich möchte wissen, was der grundlegende Zweck der Verwendung von fflush() ist und wo ich es verwenden kann. Und vor allem interessiert es mich zu wissen welche Probleme kann es bei der Verwendung von fflush() geben.

  • wenn Sie verwenden printf() ohne neue Zeile wird es wahrscheinlich nicht sofort gedruckt. Wenn Sie wissen, dass Ihr Programm jede Sekunde abstürzen kann, können Sie verwenden fflush() (oder Zeilenumbrüche …). Ich glaube aber nicht, dass das der gängige Sprachgebrauch ist.

    – Elazar

    27. Mai 2013 um 21:48 Uhr

  • Das einzige Problem, das ich mir vorstellen kann, ist, dass es schlecht für die Leistung ist, es zu oft zu tun. Außerdem sollten Sie wissen, dass das Schließen einer Datei oder das Beenden eines Programms automatisch gelöscht wird. Ein kurz laufendes Programm muss selten anrufen fflush() ausdrücklich.

    – diejh

    27. Mai 2013 um 22:04 Uhr


  • Der gesamte Zweck von stdio ist die Bereitstellung einer anwendungsseitigen Pufferung für die read() und write() Systemaufrufe. Jedes Mal, wenn Sie eine Pufferung haben, benötigen Sie einen Flush-Vorgang. Das einzige identifizierbare ‘Problem’ dabei ist, dass Sie vergessen, es zu verwenden, wenn Sie es brauchen, oder es vielleicht überbeanspruchen, z. B. innerhalb von Schleifen statt am Ende der Schleife.

    – Benutzer207421

    1. März 2017 um 0:17 Uhr

Es ist ein wenig schwer zu sagen, was bei (übermäßigem?) Gebrauch “Probleme sein kann”. fflush. Alle möglichen Dinge kann Probleme sein oder werden, abhängig von Ihren Zielen und Vorgehensweisen. Wahrscheinlich eine bessere Art, dies zu betrachten, ist, was die Absicht ist fflush ist.

Das erste, was zu berücksichtigen ist, ist das fflush ist nur für Ausgabestreams definiert. Ein Ausgabestrom sammelt “Dinge, die in eine Datei geschrieben werden sollen” in einem großen (ish) Puffer und schreibt diesen Puffer dann in die Datei. Der Zweck dieses Sammelns und Schreibens später besteht darin, die Geschwindigkeit/Effizienz auf zwei Arten zu verbessern:

  • Bei modernen Betriebssystemen gibt es einige Strafen für das Überschreiten der Benutzer-/Kernel-Schutzgrenze (das System muss einige Schutzinformationen in der CPU ändern usw.). Wenn Sie eine große Anzahl von Schreibaufrufen auf Betriebssystemebene tätigen, zahlen Sie diese Strafe für jeden einzelnen. Wenn Sie beispielsweise 8192 einzelne Schreibvorgänge in einem großen Puffer sammeln und dann einen Aufruf tätigen, entfernen Sie den größten Teil dieses Overheads.
  • Auf vielen modernen Betriebssystemen versucht jeder Schreibaufruf des Betriebssystems, die Dateileistung auf irgendeine Weise zu optimieren, zB indem festgestellt wird, dass Sie eine kurze Datei zu einer längeren erweitert haben und es gut wäre, den Plattenblock von Punkt A an zu verschieben die Diskette zu Punkt B auf der Diskette, damit die längeren Daten zusammenhängend passen. (Bei älteren Betriebssystemen ist dies ein separater “Defragmentierungs”-Schritt, den Sie möglicherweise manuell ausführen. Sie können sich das so vorstellen, als würde das moderne Betriebssystem eine dynamische, sofortige Defragmentierung durchführen.) Wenn Sie beispielsweise 500 Bytes und dann weitere 200 schreiben würden, und dann 700 und so weiter, es wird einen Großteil dieser Arbeit erledigen; aber wenn Sie einen großen Anruf mit beispielsweise 8192 Bytes tätigen, kann das Betriebssystem einen großen Block einmal zuweisen und alles dort ablegen und muss später nicht erneut defragmentieren.

Die Leute, die Ihre C-Bibliothek und ihre stdio-Stream-Implementierung bereitstellen, tun also alles, was für Ihr Betriebssystem geeignet ist, um eine “ziemlich optimale” Blockgröße zu finden und die gesamte Ausgabe in Blöcken dieser Größe zu sammeln. (Die Zahlen 4096, 8192, 16384 und 65536 sind heute oft gut, aber es hängt wirklich vom Betriebssystem und manchmal auch vom zugrunde liegenden Dateisystem ab. Beachten Sie, dass “größer” nicht immer “besser” ist: Das Streamen von Daten in Blöcken von jeweils vier Gigabyte wird wahrscheinlich schlechter abschneiden als beispielsweise in Blöcken von 64 Kbyte.)

Aber das schafft ein Problem. Angenommen, Sie schreiben in eine Datei, z. B. eine Protokolldatei mit Datums- und Zeitstempeln und Nachrichten, und Ihr Code wird später weiter in diese Datei schreiben, aber im Moment möchte er für eine Weile anhalten und warten ein Log-Analyzer liest den aktuellen Inhalt der Log-Datei. Eine Option ist die Verwendung fclose um die Protokolldatei dann zu schließen fopen erneut zu öffnen, um später weitere Daten anzuhängen. Es ist jedoch effizienter, alle ausstehenden Protokollmeldungen in die zugrunde liegende Betriebssystemdatei zu verschieben, die Datei jedoch geöffnet zu lassen. Das ist, was fflush tut.

Das Puffern schafft auch ein weiteres Problem. Angenommen, Ihr Code hat einen Fehler und stürzt manchmal ab, aber Sie sind sich nicht sicher, ob er abstürzen wird. Und angenommen, Sie haben etwas geschrieben, und das ist sehr wichtig Dies Daten gelangen an das zugrunde liegende Dateisystem. Du kannst anrufen fflush um die Daten an das Betriebssystem zu übertragen, bevor Sie Ihren potenziell schädlichen Code aufrufen, der abstürzen könnte. (Manchmal ist dies gut zum Debuggen.)

Oder nehmen Sie an, Sie arbeiten auf einem Unix-ähnlichen System und haben eine fork Systemaufruf. Dieser Aufruf dupliziert den gesamten Benutzerbereich (macht einen Klon des ursprünglichen Prozesses). Die stdio-Puffer befinden sich im Benutzerbereich, sodass der Klon über die gleichen gepufferten, aber noch nicht geschriebenen Daten verfügt, die der ursprüngliche Prozess zum Zeitpunkt des hatte fork Anruf. Auch hier ist eine Möglichkeit, das Problem zu lösen, die Verwendung fflush um gepufferte Daten herauszudrücken, kurz bevor Sie dies tun fork. Wenn vorher alles raus ist fork, es gibt nichts zu duplizieren; der frische Klon wird niemals versuchen, die gepufferten Daten zu schreiben, da sie nicht mehr existieren.

Je mehr fflush-je Sie hinzufügen, desto mehr vereiteln Sie die ursprüngliche Idee, große Datenmengen zu sammeln. Das heißt, Sie gehen einen Kompromiss ein: Große Brocken sind effizienter, verursachen aber ein anderes Problem, also treffen Sie die Entscheidung: “Seien Sie hier weniger effizient, um ein Problem zu lösen, das wichtiger ist als die bloße Effizienz”. Du rufst an fflush.

Manchmal ist das Problem einfach “die Software debuggen”. In diesem Fall statt wiederholt anzurufen fflushkönnen Sie Funktionen wie verwenden setbuf und setvbuf um das Pufferungsverhalten eines stdio-Streams zu ändern. Dies ist bequemer (weniger oder gar keine Codeänderungen erforderlich – Sie können den Set-Buffering-Aufruf mit einem Flag steuern), als viele hinzuzufügen fflush Anrufe, so dass dies als „Problem mit der Verwendung (oder übermäßiger Verwendung) von angesehen werden könnte fflush“.

Benutzer-Avatar
jaseywang

Nun, @toreks Antwort ist fast perfekt, aber es gibt einen Punkt, der nicht so genau ist.

Das erste, was zu berücksichtigen ist, ist, dass fflush nur für Ausgabeströme definiert ist.

Laut man fflush kann fflush auch in verwendet werden Eingang Ströme:

Für Ausgabestreams erzwingt fflush() ein Schreiben aller im Userspace gepufferten Daten für den gegebenen Ausgabe- oder Aktualisierungsstream über die zugrunde liegende Schreibfunktion des Streams. Bei Eingabestreams verwirft fflush() alle gepufferten Daten, die aus der zugrunde liegenden Datei abgerufen, aber nicht von der Anwendung verwendet wurden. Der offene Status des Streams bleibt davon unberührt. Wenn es also in der Eingabe verwendet wird, verwerfen Sie es einfach.

Hier ist eine Demo zur Veranschaulichung:

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

  • Später in derselben Manpage heißt es: “Die Standards spezifizieren das Verhalten für Eingabeströme nicht. Die meisten anderen Implementierungen verhalten sich genauso wie Linux.”

    – Alok–

    27. Juli 2015 um 19:42 Uhr

  • Ich werde auch anmerken, dass die Linux-Erweiterung eine gute Sache ist, aber in BSD haben wir sie in den Nichtstandard gesteckt fpurge -Funktion, die sowohl für Eingabe- als auch für Ausgabestreams definiert ist.

    – Torek

    9. November 2015 um 7:12 Uhr

fflush() leert die mit dem Stream verbundenen Puffer. Wenn Sie zB einen Benutzer einige Daten in einer sehr kurzen Zeitspanne (Millisekunden) eingeben lassen und etwas in eine Datei schreiben, können die Schreib- und Lesepuffer etwas “Restmaterial” in sich selbst haben. du rufst an fflush() dann alle Puffer zu leeren und Standardausgaben zu erzwingen, um sicherzustellen, dass die nächste Eingabe, die Sie erhalten, die ist, die der Benutzer dann gedrückt hat.

Hinweis: http://www.cplusplus.com/reference/cstdio/fflush/

  • Was können die Probleme bei der Verwendung sein?

    – Karan

    27. Mai 2013 um 22:02 Uhr

  • fflush() ist eine Standard-C-Funktion und ist unter Linux unterstützt.

    – PP

    27. Mai 2013 um 22:17 Uhr

  • Diese Antwort ist völlig falsch und deutet darauf hin fflush kann zur Eingabe verwendet werden. Es kann nicht.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    28. Mai 2013 um 2:41 Uhr

  • Es bedeutet nicht, dass Oo es sagt, dass es die Puffer leert, die mit den Streams verbunden sind um sicherzustellen, dass Ihre nächste Eingabe richtig ist

    – Chaos

    28. Mai 2013 um 14:28 Uhr

1142830cookie-checkVerstehen der Notwendigkeit von fflush() und der damit verbundenen Probleme

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy