13

Tim Bray 的文章“安全地保存数据”给我留下了一些悬而未决的问题。今天,它已经一个多月了,我还没有看到任何后续,所以我决定在这里讨论这个话题。

文章的一个观点是,在使用 FileOutputStream 时应该调用 FileDescriptor.sync() 以确保安全。起初,我很生气,因为在我做 Java 的 12 年里,我从未见过任何 Java 代码进行同步。特别是因为处理文件是一件非常基本的事情。此外,FileOutputStream 的标准 JavaDoc 从未暗示同步 (Java 1.0 - 6)。经过一番研究,我认为 ext4 实际上可能是第一个需要同步的主流文件系统。(是否存在建议显式同步的其他文件系统?)

我很欣赏对此事的一些一般性想法,但我也有一些具体问题:

  1. Android 何时会同步到文件系统?这可以是周期性的并且另外基于生命周期事件(例如,应用程序的进程进入后台)。
  2. FileDescriptor.sync() 是否负责同步元数据?那就是同步更改文件的目录。与 FileChannel.force() 进行比较。
  3. 通常,不直接写入 FileOutputStream。这是我的解决方案(你同意吗?):
    FileOutputStream fileOut = ctx.openFileOutput(file, Context.MODE_PRIVATE);
    BufferedOutputStream out = new BufferedOutputStream(fileOut);
    try {
        out.write(something);
        out.flush();
        fileOut.getFD().sync();
    } finally {
        out.close();
    }
    
4

2 回答 2

10

Android 会在需要时进行同步——例如当屏幕关闭、关闭设备等时。如果您只是查看“正常”操作,则永远不需要应用程序的显式同步。

当用户从他们的设备中取出电池(或对内核进行硬重置)并且您希望确保不会丢失任何数据时,就会出现问题。

所以首先要意识到:问题是当电源突然断电时,不可能发生干净的关机,以及在那个时候持久存储会发生什么的问题。

如果您只是编写一个独立的新文件,那么您做什么并不重要。用户可能在你写的过程中,就在你开始写之前等拔了电池。如果你不同步,那只是意味着从你写完之后拔电池还有更长的时间会丢失数据。

这里最大的问题是您何时想要更新文件。在这种情况下,当您下次读取文件时,您希望获得以前内容或内容。您不想半途而废,也不想丢失数据。

这通常通过将数据写入新文件,然后从旧文件切换到该文件来完成。在 ext4 之前,您知道,一旦您完成了文件的写入,其他文件的进一步操作将在该文件上的操作之前不会在磁盘上进行,因此您可以安全地删除以前的文件或以其他方式执行依赖于新文件的操作被完整地写入。

但是现在如果你写新文件,然后删除旧文件,然后拔掉电池,下次启动时你可能会看到旧文件被删除并创建了新文件,但新文件的内容不完整。通过进行同步,您可以确保新文件在该点已完全写入,因此可以根据该状态进行进一步的更改(例如删除旧文件)。

于 2011-01-16T22:15:18.453 回答
1

fileOut.getFD().sync();应该在 finally 子句上,在close().

sync()close()考虑耐用性更重要。

所以,每次你想“完成”一个文件的工作时,你应该sync()close()ing 之前完成它。

posix 不保证在您发出close().

于 2011-11-18T20:11:05.527 回答