我在写一个程序,某种数据库。当我阅读fclose(3)手册时,我发现它调用fflush(3)将FILE*缓冲区刷新到磁盘(实际上是到OS缓冲区,但现在没关系,我们总是可以调用fsync(2))。
因为我正在编写数据库,所以很明显,我希望防止数据丢失。如果没有磁盘空间,而fflush(3)在fclose(3)中失败,我们将丢失数据,因为
在
中出现错误后使用
FILE*将导致未定义的行为
因此,我考虑在fflush(3)之前显式使用fclose(3),警告用户磁盘空间偏低,并在一段时间后召回fflush(3)。
我读过C标准,认为这是个好主意。实际上,在失败的fflush之后,第二个调用总是返回0(没有错误),但实际上什么也不做。fsync帮不了我(我以为数据可能会保存在内存中)。
在这种情况下,如何防止数据丢失?也许有一些经验法则。
这是我的测试代码:
#include <stdio.h>
int main()
{
FILE *a = fopen("/tmp/1", "wb")
if ( !a )
perror("fopen");
if ( fwrite("test", 1, 4, a) != 4 )
perror("fwrite"); // always OK, cause data is buffered
while( fflush(a) ) // ...second call will always return 0!
{
perror("fflush"); // if there is no disk space, I will get this perror, but ...
}
if ( fclose(a) ) // always ok, because calls only close(2)
perror("fclose");
return 0;
}发布于 2010-02-07 01:05:03
随后的fflush()操作成功的原因是没有(新的)数据要写入磁盘。第一个fflush()失败了;这是悲剧性的,但历史。随后的fflush()没有什么可做的,所以它做得很成功。
如果您正在写入数据库,您必须小心每一次写入--而不仅仅是在最后处理问题。取决于您的数据有多重要,您可能需要经历各种循环来处理问题-- DBMS很复杂的原因是有原因的,而失败的写操作就是其中之一。
处理这个问题的一种方法是预先分配数据的空间。正如其他人所注意到的,经典的Unix文件系统允许稀疏文件(在这些文件中,没有为其分配磁盘空间的空块),因此您实际上必须将一些数据写入您需要分配的每个页面上。然后,你只需要担心‘磁盘满’的问题,当你扩展空间-你知道什么时候你这样做,你可以认真处理这个问题。
在基于Unix的系统上,有多种系统调用可以帮助您同步磁盘上的数据,以及“打开”等选项,其中包括“O_DSYNC”和相关值。但是,如果您正在扩展一个文件,它们仍然会导致“空间不足”的失败,即使使用了花哨的同步选项。当您遇到这个失败时,您必须等待空间变得可用(因为您要求用户告诉您何时可用),然后再试一次写。
发布于 2010-02-07 00:56:30
fflush只会将C库内部缓冲区刷新到操作系统上,因此fflush不会保证数据不会丢失。
重复调用fflush (没有中间写)不会有帮助,因为您已经将数据刷新了一次。第二个fflush调用将返回成功,因为没有什么可以冲到操作系统。如果由于硬盘已满而导致fflush失败,您已经丢失了一些数据。
要将数据刷新到磁盘,需要使用fsync。
如果硬盘已经满了,你就倒霉了。防止数据丢失的唯一方法是现在使进程(以及内存中的数据:用户空间/内核文件缓冲区中的数据)保持活动状态,直到您在磁盘上找到要fsync的空间为止。如果停电了,你就会失去数据。
简而言之,如果您的硬盘已经满了,您就无法保证没有数据丢失。
发布于 2010-02-07 01:02:55
您可以预先分配一些合理的磁盘空间。编写、刷新和同步一些二进制零(或其他什么),然后返回到原来的位置。必要时冲洗并重复。如果需要的话记得截断。
有点痛,但应该管用。
https://stackoverflow.com/questions/2215363
复制相似问题