4

所以我的应用程序经常想要保存文件以便稍后再次加载。最近因崩溃而倒霉,我想以这样一种方式编写操作,即保证我有新数据或原始数据,但没有损坏的混乱。

我的第一个想法是按照以下方式做一些事情(保存一个名为 example.dat 的文件):

  1. 为目标目录提供一个唯一的文件名,例如 example.dat.tmp
  2. 创建该文件并将我的数据写入其中。
  3. 删除原始文件(example.dat)
  4. 将临时文件重命名(“移动”)到原始文件所在的位置(example.dat.tmp -> example.dat)。

然后在加载时应用程序可以遵循以下规则:

  • 如果没有“example.dat”也没有“example.dat.tmp”,首先运行/新建项目,所以加载默认值/创建新文件。
  • 如果“example.dat”且没有“example.dat.tmp”,则加载 example.dat(正常负载情况)
  • 如果存在“example.dat.tmp”,则为用户提供潜在恢复数据的机会。如果“example.dat”也存在,请不要在没有明确用户常量的情况下覆盖它。

但是,做了一些研究后,我发现除了我可以用文件刷新方法覆盖的操作系统缓存之外,一些磁盘驱动器仍然在内部缓存,甚至可能对操作系统说它们已经完成,所以 4 . 可以完成,写入实际上并没有写入,如果系统出现故障,我已经丢失了我的数据......

我不确定磁盘问题实际上是否可以通过应用程序解决,但上述一般规则是否正确?我是否应该将文件的旧恢复副本保留更长时间以确保,关于这些事情的指导方针是什么(例如,可接受的磁盘使用情况,用户应该选择,将这些文件放在哪里等)。

另外我应该如何避免“example.dat.tmp”的用户和其他程序的潜在冲突。我记得有时会从其他一些软件中看到“~example.dat”,这是一个更好的约定吗?

4

2 回答 2

2

如果磁盘驱动器向操作系统报告数据在物理上位于磁盘上,而事实并非如此,那么您对此无能为力。许多磁盘确实缓存了一定数量的写入,并报告它们已完成,但是这些磁盘应该有备用电池,并且无论如何都会完成物理写入(并且在系统崩溃的情况下它们不会丢失数据,因为他们甚至不会看到它)。

剩下的,你说你已经做了一些研究,所以你无疑知道你不能使用std::ofstream(nor FILE*) 来做这件事;您必须在系统级别进行实际写入,并为它们打开具有特殊属性的文件以确保完全同步。否则,这些操作可能会在操作系统缓冲中停留一段时间。据我所知,没有办法确保rename. (但我不确定是否有必要,如果你总是保留两个版本:在这种情况下我通常的约定是写入一个文件"example.dat.new",然后当我完成写入时,删除任何名为的文件"example.dat.bak",重命名"example.dat""example.dat.bak",然后重命名"example.dat.new""example.dat". 鉴于此,您应该能够弄清楚发生了什么或没有发生什么,并找到正确的文件(如果需要,以交互方式,或插入带有时间戳的初始行)。

于 2013-07-26T15:19:43.233 回答
0

如果有可能不同的进程可能会通过您描述的相同协议,则应在编写其替代文件时锁定实际数据文件。

您可以flock用于文件锁定。

至于您的临时文件名,您可以将您的进程 ID 作为其中的一部分,例如“example.dat.3124”,没有其他同时运行的进程会生成相同的名称。

于 2013-07-26T15:00:13.747 回答