简体   繁体   English

为什么 fclose() 并不总是将数据刷新到磁盘?

[英]why fclose() is not always flushing the data to the disk?

I'm getting some weird results, while trying to write data to files in C.<br/> I thought that fclose() closes the *FILE and flushes the data from its buffer to the file.<br/> But for some reason it only flushes the data in my program sometimes and it doesn't do it other times.在尝试将数据写入C.<br/>我认为fclose()会关闭*FILE并将数据从其缓冲区flushesfile.<br/>但是对于某些人来说因为它有时只刷新我程序中的数据,而其他时候不这样做。

For example: I would run this code, and in my file I can see the two strings.例如:我会运行这段代码,在我的文件中我可以看到这两个字符串。 (Perfect, exactly what I want) (完美,正是我想要的)
But then when I run the code the next 4 times it doesn't change anything in my file.但是当我接下来运行代码 4 次时,它并没有改变我文件中的任何内容。 And then when I run it another time, suddenly the 10 extra strings appear (8 from the last times I ran the program and the 2 from now)然后当我再次运行它时,突然出现了 10 个额外的字符串(我上次运行程序时有 8 个,现在有 2 个)
(This 4 times is just an example, sometimes it's 5, 8, 10, or even just 2 times before I see the output appear) (这4次只是一个例子,有时是5、8、10,甚至只是2次才看到output出现)

I really don't understand this?我真的不明白这个? Shouldn't the data be visible after every time I run the program?每次运行程序后数据不应该可见吗? Where is this buffer even saved between the different times I run the program, because the program finishes every time, so the memory gets released, right?在我运行程序的不同时间之间,这个缓冲区甚至保存在哪里,因为程序每次都完成,所以 memory 被释放,对吧?

(By the way I also tried fflush(fd) before and after fclose(), but that didn't solve the problem) (顺便说一句,我在 fclose() 之前和之后也尝试过fflush(fd) ,但这并没有解决问题)

#include <stdio.h>


int main(int argc, char const *argv[]) {
  FILE * fd;
  fd = fopen("test_file.txt", "a");
  fprintf(fd, "String 1\n");
  fprintf(fd, "String 2\n");
  fclose(fd);
  return 0;
}

Thanks to michael kerrisk we have all the answers we need in linux man pages .感谢michael kerrisk ,我们在linux 手册页中获得了我们需要的所有答案。 Will if you dig in the man pages and see the Notes section (listed below in my answer) you will understand this behavior.如果您在手册页中挖掘并查看“注释”部分(在我的答案中列出),您会理解这种行为。

int fclose(FILE *stream); - close a file stream pointed by stream. - 关闭 stream 指向的文件 stream。

DESCRIPTION描述

The fclose() function flushes the stream pointed to by stream (writing any buffered output data using fflush() ) and closes the underlying file descriptor. The fclose() function flushes the stream pointed to by stream (writing any buffered output data using fflush() ) and closes the underlying file descriptor.

NOTES笔记

Note that fclose() flushes only the user-space buffers provided by the C library.请注意, fclose()仅刷新 C 库提供的user-space buffers To ensure that the data is physically stored on disk the kernel buffers must be flushed too , for example, with sync() or fsync() .为了确保数据物理存储在磁盘上, kernel 缓冲区也必须被刷新,例如使用sync()fsync()

So its not enough to ensure writing and flushing your buffers, we need to flush the kernel buffers as well.因此,它不足以确保写入和刷新缓冲区,我们还需要刷新 kernel 缓冲区。 how?如何? see below: make sure that you are reading my comments in the code as well!见下文:确保您也在阅读我在代码中的评论!

#include <unistd.h>
#include <stdio.h>

int main(int argc, char const *argv[]) {
  FILE * fd;
  fd = fopen("test_file.txt", "a");
  fprintf(fd, "String 1\n");
  fprintf(fd, "String 2\n");
  
  if(fclose(fd) != 0){
/*********************************************************************
fclose failed and errno will be set to endicate the error. be aware 
that we shall 
not call fsync or any other stream manipulation on fd in this case 
because we will get undefined behavior !!!
**********************************************************************/
      ... do work but no more work on this stream (fd)...
  }

  /* ~~ be aware that this is a blocking call ! (see details below) ~~*/
  if(fsync(fd) == -1){
      /*fsync fails*/
      ....
  } 
  return 0;
}

fsync() transfers ( "flushes" ) all modified in-core data of (ie, modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device (or other permanent storage device) so that all changed information can be retrieved even if the system crashes or is rebooted . fsync()将文件描述符fd引用的文件的所有修改的内核数据(即修改的缓冲区缓存页面)传输( "flushes" )到磁盘设备(或其他永久存储设备),以便所有更改的信息即使系统崩溃或重新启动也可以找回。 This includes writing through or flushing a disk cache if present.这包括写入或刷新磁盘缓存(如果存在)。 The call blocks until the device reports that the transfer has completed The call blocks ,直到设备报告传输已完成

One more important thing, always prefer calling fsync(fd) rather than calling void sync(void);更重要的一件事,总是更喜欢调用fsync(fd)而不是调用void sync(void); ? ? why ?为什么 ?

because sync() causes all pending modifications to filesystem metadata and cached file data to be written to the underlying filesystems.因为sync()会导致对文件系统元数据和缓存文件数据的所有挂起修改写入底层文件系统。 usualy we dont want this kind of behavior (and this trigger the kernel to do extra work that is not essential. so dont call it.通常我们不希望这种行为(这会触发 kernel 做不必要的额外工作。所以不要调用它。

this page and answer is not large enough to include all the details and special cases and all error codes: please refer to man pages and to the links belwo:此页面和答案不够大,无法包含所有详细信息和特殊情况以及所有错误代码:请参阅手册页和以下链接:

fclose关闭

fsync同步

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM