【问题标题】:Should fsync be used after each fclose?每次 fclose 后都应该使用 fsync 吗?
【发布时间】:2017-05-05 10:34:09
【问题描述】:

在 Linux(Ubuntu 平台)设备上,我使用文件来保存关键任务数据。

有时(大约 10,000 例中出现一次),文件会因不明原因而损坏。 特别是,文件被截断(而不是一些 kbyte,它只有大约 100 个字节)。

现在,在软件的序列中

  1. 文件已打开,
  2. 修改和
  3. 关闭。

紧接着,文件可能会再次打开 (4),并且正在执行其他操作。

到目前为止,我还没有注意到 fflush(在 fclose 上调用)不会写入文件系统,而只会写入中间缓冲区。可能是因为 3) 和 4) 之间的时间太短,并且从 2) 的更改尚未写入光盘,所以当我用 4) 重新打开时,我得到一个截断的文件,当它再次关闭时会导致这些数据永久丢失?

在这种情况下,我应该在每个文件写入后使用fsync() 吗?

停电时我必须考虑什么?数据损坏与掉电有关的可能性不大。

【问题讨论】:

  • 您不能打开、修改、写入临时文件、关闭、刷新、删除原始文件(或移动到备份文件夹)、重命名吗?
  • 另外,请确保您正在检查来自 fclose() 的返回值。由于它隐式调用 fflush() 来写入数据,因此它可能会失败,就像任何写入数据的 stdio 调用都可能失败一样。

标签: c linux file stdio


【解决方案1】:

fwrite 首先写入内部缓冲区,然后有时(在fflushfclose 或缓冲区已满时)调用操作系统函数write

操作系统也在做一些缓冲,写入设备可能会延迟。

fsync 确保操作系统正在将其缓冲区写入设备。

在您打开-写入-关闭的情况下,您不需要fsync。操作系统知道文件的哪些部分尚未写入设备。因此,如果第二个进程想要读取文件,操作系统知道它在内存中有文件内容,并且不会从设备中读取文件内容。

当然,在考虑停电时,fsync 可能是一个好主意,以确保将文件内容写入设备(正如 Andrew 指出的那样,这并不一定意味着内容会写入磁盘,因为设备本身可能会进行缓冲)。

【讨论】:

  • fsync 以确保将文件内容写入磁盘。 但这只是意味着磁盘设备(无论它可能是什么 - RAID 控制器或实际磁盘)已接受数据。这并不意味着数据被安全存储——磁盘设备也在内存中缓存数据。
【解决方案2】:

到目前为止,我还没有注意到 fflush(在 fclose 上调用)不会写入文件系统,而只会写入中间缓冲区。可能是因为 3) 和 4) 之间的时间太短,并且从 2) 的更改尚未写入光盘,所以当我用 4) 重新打开时,我得到一个截断的文件,当它再次关闭时会导致这些数据永久丢失?

没有。以这种方式运行的系统将无法使用。

在这种情况下,我应该在每次写入文件后使用 fsync() 吗?

不,这只会减慢速度。

停电时我必须考虑什么?数据损坏与断电有关。

使用能够抵抗此类损坏的文件系统。甚至可以考虑使用更安全的修改算法,例如用不同的名称写出文件的新版本,同步,然后在现有文件的顶部重命名。

【讨论】:

  • '更安全的修改算法' - 是的,这正是我会做的。
【解决方案3】:

如果你正在做的事情是这样的:

FILE *f = fopen("filename", "w");
while(...) {
    fwrite(data, n, m, f);
}
fclose(f);

然后可能发生的情况是另一个进程可以在写入文件时打开文件(在 C 库在后台运行的 openwrite 系统调用之间,或在单独的 write 调用之间)。然后他们只会看到一个部分写入的文件。

解决方法是使用另一个名称写入文件,并将rename() 覆盖实际文件名。缺点是您需要双倍的空间。

如果您确定文件的打开仅发生在写入之后,那么这不可能发生。但是,作者和读者之间必须有一些同步,这样后者才不会过早开始阅读。

fsync() 告诉系统将更改写入实际存储,这在其他 POSIX 系统调用中有点奇怪,因为我认为如果系统崩溃,则没有指定任何系统,这是唯一的情况如果某些数据存储在实际存储中而不是某些缓存中,这很重要。即使使用fsync(),存储硬件仍然有可能缓存数据,或者当系统崩溃时,不相关的损坏会破坏文件系统。

如果您乐于让操作系统完成它的工作,并且不需要考虑崩溃,您可以完全忽略fsync(),只在操作系统认为合适时写入数据。如果您确实关心崩溃,则必须更仔细地研究文件系统提供(或不提供)什么保证。例如。至少在某些时候,ext* 开发人员几乎要求应用程序也在包含目录上执行fsync()

【讨论】:

  • fflush 是 c 流库的一部分。他使用的是 fopen fclose,而不是 open close。
  • @RobertJacobs,没关系,最迟在关闭文件时,C 库将不得不调用write()。无论如何,已编辑以显示 stdio 调用。
猜你喜欢
  • 2019-08-15
  • 2011-08-25
  • 2016-07-06
  • 2017-10-13
  • 2016-06-30
  • 2012-01-07
  • 2020-07-24
  • 1970-01-01
  • 2010-10-23
相关资源
最近更新 更多