检查文件内容是否已实际写入磁盘 - 未在磁盘控制器的缓冲区中排队

Check if file content has been actually written to disk - not being queued in disk controller's buffer

本文关键字:磁盘控制器 排队 缓冲区 磁盘 文件 是否 检查      更新时间:2023-10-16

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

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");
}

我用两个分别为5mb的输入文件反复测试了这个程序。有一次,我突然拔掉了电源线,关闭了系统。重新启动系统后,我检查了数据,发现输入文件被删除,file_out.dat被全0填满。这让我相信,在删除2个输入文件后,系统就会崩溃,而输出数据仍然在磁盘控制器的缓冲区中的某个地方。如果这是真的,那么我是否有办法检查数据是否已经写入磁盘?

一般情况下不需要。即使你告诉操作系统等待数据写入(使用sync API家族),一些磁盘对操作系统撒谎,声称写入完成,而实际上它只是在硬盘的板载RAM缓存中排队,这将在突然断电时丢失。

你能做的最好的就是明确地要求操作系统告诉磁盘"真的,真的同步所有东西,然后阻塞直到完成"。在执行了fflush之后(它只告诉stdio库将所有用户模式缓冲的数据发送到操作系统,操作系统通常将其保存在内核缓冲区中,然后在后台将内核缓冲区同步到磁盘),要么使用fsync限制范围,要么使用syncsyncfs(前者同步所有文件系统,后者将范围限制到对应于单个文件描述符的文件系统)。

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

  1. 在最终的fflush之后但在rename之前做一个目标fsync(这样新文件在替换旧文件之前在磁盘上完成),和
  2. rename之后但在remove调用之前做更广泛的sync/syncfs(因此在删除源文件之前,rename的元数据更新已经完成)

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

当然,就像我说的,这是尽最大努力;如果磁盘控制器欺骗了操作系统,那么除了为磁盘编写新的固件和驱动程序之外,你什么也做不了,这可能太过分了。