Verständnis für die Notwendigkeit von fflush () und damit verbundenen Problemen

8

Es folgt ein Beispielcode für die Verwendung von fflush ():

%Vor%

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

    
Karan Mer 27.05.2013, 21:45
quelle

3 Antworten

35

Es ist ein bisschen schwer zu sagen, was "Probleme mit" (übermäßiger?) Verwendung von fflush sein kann. Alle möglichen Dinge können Probleme sein oder werden, abhängig von Ihren Zielen und Ansätzen. Wahrscheinlich ist die Absicht von fflush besser, dies zu betrachten.

Zunächst ist zu beachten, dass fflush nur für Ausgabeströme definiert ist. Ein Ausgabestrom sammelt "Dinge, die in eine Datei geschrieben werden sollen" in einen großen (ish) Puffer und schreibt diesen Puffer dann in die Datei. Der Sinn dieses späteren Sammelns und Schreibens besteht darin, die Geschwindigkeit / Effizienz auf zwei Arten zu verbessern:

  • Auf modernen Betriebssystemen gibt es einige Nachteile 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 Schreibanrufen auf Betriebssystemebene tätigen, zahlen Sie diese Strafe für jeden einzelnen. Wenn Sie etwa 8192 individuelle Schreibvorgänge in einem großen Puffer sammeln und dann einen Anruf tätigen, entfernen Sie den größten Teil dieses Aufwands.
  • Auf vielen modernen Betriebssystemen wird jeder Schreibaufruf des Betriebssystems versuchen, die Dateileistung auf irgendeine Weise zu optimieren, z. B. indem Sie feststellen, dass Sie eine kurze Datei auf eine längere Datei erweitert haben, und es wäre gut, den Festplattenblock zu verschieben Punkt A auf der Festplatte zu Punkt B auf der Festplatte, so dass die längeren Daten zusammenhängend passen. (Auf älteren Betriebssystemen ist dies ein separater "Defragmentierungsschritt", den Sie möglicherweise manuell ausführen. Sie können sich das als das moderne Betriebssystem vorstellen, das eine dynamische, sofortige Defragmentierung durchführt.) Wenn Sie beispielsweise 500 Bytes und dann weitere 200 schreiben würden, und dann 700 und so weiter, es wird viel von dieser Arbeit machen; aber wenn Sie einen großen Aufruf mit, sagen wir, 8192 Bytes machen, kann das OS einmal einen großen Block zuweisen und alles dort ablegen und muss nicht später erneut defragmentieren.

Die Leute, die Ihre C-Bibliothek und ihre stdio-Stream-Implementierung bereitstellen, tun also alles, was auf Ihrem Betriebssystem angemessen ist, um eine "einigermaßen 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 sehr 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 Streaming von Daten in Blöcken von jeweils vier Gigabyte wird wahrscheinlich schlechter abschneiden als zum Beispiel in Blöcken von 64 KB.)

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 weiterhin in diese Datei schreiben, aber momentan möchte er eine Weile anhalten und zulassen Ein Log-Analyzer liest den aktuellen Inhalt der Log-Datei. Eine Option besteht darin, fclose zu verwenden, um die Protokolldatei zu schließen, und fopen , um sie erneut zu öffnen, um später weitere Daten anzuhängen. Es ist jedoch effizienter, ausstehende Protokollmeldungen in die zugrunde liegende BS-Datei zu übertragen, die Datei jedoch geöffnet zu lassen. Das macht fflush .

Die Pufferung verursacht auch ein anderes Problem. Angenommen, Ihr Code enthält einen Fehler, der manchmal abstürzt, aber Sie sind sich nicht sicher, ob der Code abstürzen wird. Und angenommen, Sie haben etwas geschrieben, und es ist sehr wichtig, dass diese Daten in das zugrunde liegende Dateisystem gelangen. Sie können fflush aufrufen, um die Daten an das Betriebssystem zu senden, bevor Sie Ihren möglicherweise fehlerhaften Code aufrufen, der abstürzen könnte. (Manchmal ist dies zum Debuggen gut.)

Oder angenommen, Sie befinden sich auf einem Unix-ähnlichen System und haben einen fork Systemaufruf. Dieser Aufruf dupliziert den gesamten Benutzerbereich (erstellt einen Klon des ursprünglichen Prozesses). Die stdio-Puffer befinden sich im Benutzerbereich, so dass der Klon die gleichen gepufferten, aber noch nicht geschriebenen Daten hat, die der ursprüngliche Prozess zum Zeitpunkt des Aufrufs fork hatte. Auch hier ist eine Möglichkeit, das Problem zu lösen, die Verwendung von fflush , um gepufferte Daten kurz vor der Ausführung von fork zu löschen. Wenn alles vor dem fork steht, gibt es nichts zu duplizieren; Der frische Klon wird niemals versuchen, die gepufferten Daten zu schreiben, da sie nicht mehr existieren.

Je mehr fflush -es hinzugefügt werden, desto mehr vereiteln Sie die ursprüngliche Idee, große Datenmengen zu sammeln. Das heißt, Sie machen einen Kompromiss: 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 bloße Effizienz". Sie rufen fflush auf.

Manchmal ist das Problem einfach "Debuggen der Software". In diesem Fall können Sie, anstatt wiederholt fflush aufzurufen, Funktionen wie setbuf und setvbuf verwenden, um das Pufferverhalten eines stdio-Streams zu ändern. Dies ist bequemer (weniger oder gar keine Codeänderungen erforderlich - Sie können den Set-Pufferungsaufruf mit einem Flag steuern) als das Hinzufügen einer großen Menge von fflush -Aufrufen, so dass dies als ein "Problem bei der Verwendung" (oder übermäßig -use) von fflush ".

    
torek 27.05.2013, 23:31
quelle
1

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

  

Als erstes ist zu beachten, dass fflush nur für die Ausgabe definiert ist   Streams.

Laut fflush kann fflush auch in input Streams verwendet werden:

  

Bei Ausgabeströmen erzwingt fflush () das Schreiben des gesamten Benutzerbereichs   gepufferte Daten für den gegebenen Ausgabe- oder Update-Stream über die Streams   zugrunde liegende Schreibfunktion. Für          In Eingabe-Streams verwirft fflush () alle gepufferten Daten, die von der zugrunde liegenden Datei abgerufen, aber nicht von ihnen konsumiert wurden   die Anwendung Der offene Status von          Der Stream ist nicht betroffen.   Wenn es in der Eingabe verwendet wird, fflush einfach wegwerfen.

Hier ist eine Demo, um es zu illustrieren:

%Vor%     
jaseywang 02.03.2015 15:17
quelle
-1

fflush() leert die mit dem Stream verbundenen Puffer. wenn Sie z.B. Wenn ein Benutzer einige Daten in einer sehr kurzen Zeitspanne (Millisekunden) eingibt und etwas in eine Datei schreibt, können die Schreib- und Lesepuffer etwas "Rest" in sich behalten. Sie rufen dann fflush() auf, um alle Puffer zu leeren und die Standardausgaben zu erzwingen, um sicher zu sein, dass die nächste Eingabe, die Sie erhalten, diejenige ist, die der Benutzer dann gedrückt hat.

Referenz: Ссылка

    
TheOneAndOnly 27.05.2013 21:59
quelle

Tags und Links