C语言 了解对 fflush() 的需求以及与之相关的问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16780908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:29:20  来源:igfitidea点击:

Understanding the need for fflush() and problems associated with it

cfflush

提问by karan

Below is sample code for using 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);
}

All I know about fflush() is that it is a library function used to flush an output buffer. I want to know what is the basic purpose of using fflush(), and where can I use it. And mainly I am interested in knowing what problems can there be with using fflush().

我所知道的 fflush() 是一个用于刷新输出缓冲区的库函数。我想知道使用 fflush() 的基本目的是什么,我在哪里可以使用它。主要是我想知道使用 fflush() 会出现什么问题。

回答by torek

It's a little hard to say what "can be problems with" (excessive?) use of fflush. All kinds of things canbe, or become, problems, depending on your goals and approaches. Probably a better way to look at this is what the intent of fflushis.

很难说“可能有问题”(过度?)使用fflush. 根据您的目标和方法,各种事情可能成为或成为问题。可能更好的方式来看待这就是意图是什么fflush

The first thing to consider is that fflushis defined only on output streams. An output stream collects "things to write to a file" into a large(ish) buffer, and then writes that buffer to the file. The point of this collecting-up-and-writing-later is to improve speed/efficiency, in two ways:

首先要考虑的是它fflush仅在输出流上定义。输出流将“要写入文件的内容”收集到一个大(ish)缓冲区中,然后将该缓冲区写入文件。这种收集和写入稍后的重点是通过两种方式提高速度/效率:

  • On modern OSes, there's some penalty for crossing the user/kernel protection boundary (the system has to change some protection information in the CPU, etc). If you make a large number of OS-level write calls, you pay that penalty for each one. If you collect up, say, 8192 or so individual writes into one large buffer and then make one call, you remove most of that overhead.
  • On many modern OSes, each OS write call will try to optimize file performance in some way, e.g., by discovering that you've extended a short file to a longer one, and it would be good to move the disk block from point A on the disk to point B on the disk, so that the longer data can fit contiguously. (On older OSes, this is a separate "defragmentation" step you might run manually. You can think of this as the modern OS doing dynamic, instantaneous defragmentation.) If you were to write, say, 500 bytes, and then another 200, and then 700, and so on, it will do a lot of this work; but if you make one big call with, say, 8192 bytes, the OS can allocate a large block once, and put everything there and not have to re-defragment later.
  • 在现代操作系统上,跨越用户/内核保护边界会受到一些惩罚(系统必须更改 CPU 中的某些保护信息等)。如果您进行大量操作系统级别的写入调用,您将为每个调用付出代价。如果您将大约 8192 个单独的写入收集到一个大缓冲区中,然后进行一次调用,则可以消除大部分开销。
  • 在许多现代操作系统上,每个操作系统写入调用都会尝试以某种方式优化文件性能,例如,通过发现您已将一个短文件扩展为一个较长的文件,并且最好将磁盘块从 A 点移动到将磁盘指向磁盘上的 B,以便可以连续容纳较长的数据。(在较旧的操作系统上,这是一个单独的“碎片整理”步骤,您可以手动运行。您可以将其视为现代操作系统进行动态、即时的碎片整理。)如果您要写入 500 个字节,然后再写入 200 个字节,然后是 700 等等,它会做很多这样的工作;但是如果你用 8192 字节进行一次大调用,操作系统可以一次分配一个大块,然后把所有东西都放在那里,以后不必重新整理碎片。

So, the folks who provide your C library and its stdio stream implementation do whatever is appropriate on your OS to find a "reasonably optimal" block size, and to collect up all output into chunk of that size. (The numbers 4096, 8192, 16384, and 65536 often, today, tend to be good ones, but it really depends on the OS, and sometimes the underlying file system as well. Note that "bigger" is not always "better": streaming data in chunks of four gigabytes at a time will probably perform worse than doing it in chunks of 64 Kbytes, for instance.)

因此,提供您的 C 库及其 stdio 流实现的人会在您的操作系统上做任何适当的事情,以找到“合理最佳”的块大小,并将所有输出收集到该大小的块中。(数字 4096、8192、16384 和 65536 在今天往往是好的,但这确实取决于操作系统,有时也取决于底层文件系统。请注意,“更大”并不总是“更好”:例如,一次以 4 GB 的块流式传输数据的性能可能比以 64 KB 的块传输更差。)

But this creates a problem. Suppose you're writing to a file, such as a log file with date-and-time stamps and messages, and your code is going to keep writing to that file later, but right now, it wants to suspend for a while and let a log-analyzer read the current contents of the log file. One option is to use fcloseto close the log file, then fopento open it again in order to append more data later. It's more efficient, though, to push any pending log messages to the underlying OS file, but keep the file open. That's what fflushdoes.

但这会产生一个问题。假设您正在写入一个文件,例如带有日期和时间戳记和消息的日志文件,并且您的代码稍后将继续写入该文件,但现在,它想要暂停一段时间并让日志分析器读取日志文件的当前内容。一种选择是使用fclose关闭日志文件,然后fopen再次打开它以便稍后附加更多数据。不过,将任何挂起的日志消息推送到底层 OS 文件但保持文件打开的效率更高。这就是fflush它的作用。

Buffering also creates another problem. Suppose your code has some bug, and it sometimes crashes but you're not sure if it's about to crash. And suppose you've written something and it's very important that thisdata get out to the underlying file system. You can call fflushto push the data through to the OS, before calling your potentially-bad code that might crash. (Sometimes this is good for debugging.)

缓冲还会产生另一个问题。假设您的代码有一些错误,它有时会崩溃,但您不确定它是否即将崩溃。假设您已经写了一些东西,并且将这些数据传送到底层文件系统非常重要。fflush在调用可能崩溃的潜在错误代码之前,您可以调用将数据推送到操作系统。(有时这有利于调试。)

Or, suppose you're on a Unix-like system, and have a forksystem call. This call duplicates the entire user-space (makes a clone of the original process). The stdio buffers are in user space, so the clone has the same buffered-up-but-not-yet-written data that the original process had, at the time of the forkcall. Here again, one way to solve the problem is to use fflushto push buffered data out just before doing the fork. If everything is out before the fork, there's nothing to duplicate; the fresh clone won't ever attempt to write the buffered-up data, as it no longer exists.

或者,假设您在一个类 Unix 系统上,并且有一个fork系统调用。此调用复制整个用户空间(克隆原始进程)。stdio 缓冲区位于用户空间中,因此在fork调用时,克隆具有与原始进程相同的已缓冲但尚未写入的数据。同样,解决问题的一种方法是fflush在执行fork. 如果一切都在 之前fork,则没有什么可复制的;新的克隆永远不会尝试写入缓冲数据,因为它不再存在。

The more fflush-es you add, the more you're defeating the original idea of collecting up large chunks of data. That is, you are making a tradeoff: large chunks are more efficient, but are causing some other problem, so you make the decision: "be less efficient here, to solve a problem more important than mere efficiency". You call fflush.

fflush-es您添加,更多的你击败收藏起来,大量数据的最初的想法。也就是说,您正在权衡:大块更有效,但会导致一些其他问题,因此您做出决定:“在这里效率较低,解决比单纯效率更重要的问题”。你打电话fflush

Sometimes the problem is simply "debug the software". In that case, instead of repeatedly calling fflush, you can use functions like setbufand setvbufto alter the buffering behavior of a stdio stream. This is more convenient (fewer, or even no, code changes required—you can control the set-buffering call with a flag) than adding a lot of fflushcalls, so that could be considered a "problem with use (or excessive-use) of fflush".

有时问题只是“调试软件”。在这种情况下,fflush您可以使用像setbuf和 之类的函数setvbuf来改变 stdio 流的缓冲行为,而不是重复调用。这比添加大量fflush调用更方便(更少,甚至不需要更改代码 - 您可以使用标志控制设置缓冲调用),因此可以将其视为“使用(或过度使用)问题”的fflush“。

回答by jaseywang

Well, @torek's answer is almost perfect, but there's one point which is not so accurate.

好吧,@torek 的回答几乎是完美的,但有一点不太准确。

The first thing to consider is that fflush is defined only on output streams.

首先要考虑的是 fflush 仅在输出流上定义。

According to man fflush, fflush can also be used in inputstreams:

根据 man fflush,fflush 也可以用于输入流:

For output streams, fflush() forces a write of all user-space buffered data for the given output or update stream via the stream's underlying write function. For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application. The open status of the stream is unaffected. So, when used in input, fflush just discard it.

对于输出流, fflush() 通过流的底层写入函数强制写入给定输出或更新流的所有用户空间缓冲数据。 对于输入流, fflush() 丢弃已从底层文件中获取但尚未被应用程序使用的任何缓冲数据。流的打开状态不受影响。所以,当在输入中使用时,fflush 只是丢弃它。

Here is a demo to illustrate it:

这是一个演示来说明它:

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

回答by TheOneAndOnly

fflush()empties the buffers related to the stream. if you e.g. let a user input some data in a very shot timespan (milliseconds) and write some stuff into a file, the writing and reading buffers may have some "reststuff" remaining in themselves. you call fflush()then to empty all the buffers and force standard outputs to be sure the next input you get is what the user pressed then.

fflush()清空与流相关的缓冲区。例如,如果您让用户在非常短的时间跨度(毫秒)内输入一些数据并将一些内容写入文件,则写入和读取缓冲区可能会保留一些“剩余内容”。您调用fflush()then 以清空所有缓冲区并强制标准输出以确保您获得的下一个输入是用户当时按下的输入。

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

参考:http: //www.cplusplus.com/reference/cstdio/fflush/