【问题标题】:Check if file content has been actually written to disk - not being queued in disk controller's buffer检查文件内容是否已实际写入磁盘 - 未在磁盘控制器的缓冲区中排队
【发布时间】:2017-02-08 13:00:09
【问题描述】:

我编写了一个程序,将两个小文件压缩成一个更大的文件。我首先从输入文件中读取数据,合并数据,然后将输出写入临时文件。完成后,我将临时文件重命名为所需的文件名(位于磁盘上的同一分区中)。这是伪代码:

FILE* fp_1 = fopen("file_1.dat", "r+b");
FILE* fp_2 = fopen("file_2.dat", "r+b");
FILE* fp_out = fopen("file_tmp.dat", "w+b");

// 1. Read data for the key in two files
const char* data_1 = ...;
const char* data_2 = ...;

// 2. Merge data, store in an allocated buffer

// 3. Write merged buffer to temp file
fwrite(temp_buff, estimated_size, 1, fp_out);
fflush(fp_out);

fclose(fp_1);
fclose(fp_2);
fclose(fp_out);

// Now rename temp file to desired file name
if(std::rename("file_tmp.dat", "file_out.dat") == 0)
{
    std::remove("file_1.dat");
    std::remove("file_2.dat");
}

我用两个 5 MB 的输入文件反复测试了该程序。有一次我突然拔掉电源线关闭了系统。重新启动系统后我检查了数据,发现输入文件被删除,file_out.dat 被全零填充。这让我相信系统在删除 2 个输入文件后立即停机,并且输出数据仍在磁盘控制器缓冲区的某个位置。如果这是真的,那么我有什么方法可以检查数据是否已实际写入磁盘?

【问题讨论】:

  • 看到操作系统甚至不确定(磁盘可能有自己的 RAM 缓存),这是一个棘手的问题。

标签: c++ file-handling stdio


【解决方案1】:

一般情况下不会。即使您告诉操作系统等到数据被写入(使用sync API 系列),一些磁盘也会对操作系统说谎,当它实际上只是在硬盘驱动器的板载 RAM 缓存中排队时声称写入已完成,这将是因突然断电而丢失。

在你执行fflush 之后,你能做的最好的事情就是明确要求操作系统告诉磁盘“真的、真的同步所有内容并阻塞直到完成”(这只告诉 stdio 库发送所有用户-mode 将数据缓冲到操作系统,通常将其保存在内核缓冲区中,并稍后在后台将内核缓冲区同步到磁盘),或者使用 fsync 的有限范围或使用类似 sync or syncfs 的东西(前者同步所有文件系统, 后者将范围限制为单个文件描述符对应的文件系统)。

为了最大程度的安全,您需要:

  1. 在最后一个 fflush 之后但在 rename 之前执行一个目标 fsync(因此新文件在替换旧文件之前在磁盘上是完整的),并且
  2. 在rename 之后但在remove 调用之前执行更广泛的sync/syncfs(因此在删除源文件之前,来自rename 的元数据更新已完成)

如果您不介意在输入数据仍然存在的情况下损坏的输出数据,则可以省略第 1 步。

当然,就像我说的,这都是尽力而为;如果磁盘控制器对操作系统撒谎,那么您无能为力为磁盘编写新的固件和驱动程序,这可能太过分了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-05
    • 2012-06-19
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 2012-03-17
    • 2016-12-21
    相关资源
    最近更新 更多