我有一个由脚本调用的程序。该程序将大量数据写入磁盘上的文件,然后终止。一旦完成运行,脚本就会切断整个系统的电源。
我遇到的问题是文件没有被完整写入。如果它是一个 4GiB 的文件,当我稍后查看它时,实际上只有大约 2GiB 会在磁盘上。我能够可靠地确保写入所有数据的唯一方法是在程序完成后在退出前让程序休眠一小段时间,但这是一个非常糟糕且不可靠的黑客,我不想使用。这是我最近尝试的一些示例代码:
int main () {
FILE *output;
output = fopen("/logs/data", "w");
[fwrite several GiB of data to output]
fflush(output);
int fdo = open("/logs", O_RDONLY);
fsync(fdo);
fclose(output);
close(fdo);
return 0;
}
我最初尝试使用文件描述符构建我的FILE ,并在使用的描述符(/logs/data)上调用fsync(),但是这产生了同样的问题。根据fsync(2)的规范:
调用 fsync() 不一定确保包含该文件的目录中的条目也已到达磁盘。为此,还需要在目录的文件描述符上显式 fsync()。
这导致我使用上面的代码,仅为包含我的数据文件的目录创建一个特定的文件描述符并在其上调用fsync()。然而结果是一样的。我真的不明白为什么会这样,因为fsync()应该是阻塞的:
呼叫阻塞,直到设备报告传输已完成。
另外,如您所见,我在FILE上添加了一个fflush() ,认为fsync()可能只是同步先前已刷新的数据,但这对情况没有任何影响。
在结束程序之前,我需要以某种方式验证数据实际上是否已写入物理介质,但我不知道该怎么做。我看到有一些文件,例如 /sys/block/[device]/[partition]/stat 可以告诉我还有多少脏块要写入,我可以等待该值达到 0 但这不会似乎是解决应该是一个简单问题的好方法,此外,如果磁盘上正在运行任何其他程序,那么我也不想等待他们同步数据,因为我只关心完整性此特定文件和 stat 文件不区分。
编辑 根据建议,我尝试fsync()两次,首先在文件上,然后在目录上:
int main () {
FILE *output;
int fd = open("/logs/data", O_WRONLY | O_CREAT, 660);
output = fdopen(fd, "w");
[fwrite several GiB of data to output]
fsync(fd);
int fdo = open("/logs", O_RDONLY);
fsync(fdo);
fclose(output);
close(fd);
close(fdo);
return 0;
}
这产生了一些有趣的输出。对于一个 4GiB(4294967296 字节)的文件,磁盘上的实际数据大小为 4294963200,恰好与总值相差 1 个页面文件(4096 字节)。它似乎非常接近一个可行的解决方案,但它仍然不能保证每一个数据字节。