Android(Java)中的ext4 / fsync情况不清楚

时间:2011-01-16 19:52:21

标签: java android fileoutputstream fsync ext4

蒂姆布雷的文章"Saving Data Safely"给我留下了悬而未决的问题。今天已经超过一个月了,我还没有看到任何跟进,所以我决定在这里讨论这个话题。

本文的一点是,在使用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();
    }
    

2 个答案:

答案 0 :(得分:10)

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

问题出现在用户将电池从设备中取出(或内核硬重置),并且您希望确保不丢失任何数据。

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

如果您只是编写一个独立的新文件,那么您的工作并不重要。用户可以在写入过程中,在开始写入之前等时拉电池。如果你不同步,这只意味着从你完成写入到拉电池的时间有一些更长的时间将丢失数据。

这里最关心的是您想要更新文件。在这种情况下,当您下次读取文件时,您希望 以前的内容, new 内容。你不希望得到中途写的东西,或者丢失数据。

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

但是现在如果您编写新文件,然后删除旧文件,并且电池被拉,当您下次启动时,您可能会看到旧文件被删除并且新文件已创建,但新文件的内容不是完成。通过执行同步,您可以确保在该点完全写入新文件,因此可以进行依赖于该状态的进一步更改(例如删除旧文件)。

答案 1 :(得分:1)

fileOut.getFD().sync();应位于close()之前的finally子句中。

考虑到耐久性,

sync()close()更重要。

因此,每当您想要“完成”文件处理时,您应该在sync()之前close()

posix不保证在您发出close()时将挂起的写入内容写入磁盘。