本论坛为只读模式,仅供查阅,不能注册新用户,不能发帖/回帖,有问题可发邮件 xikug.xp (^) gmail.com
查看: 7889|回复: 7

fwrite可能会造成写入文件的数据不完整(丢失) [复制链接]

Rank: 9Rank: 9Rank: 9

发表于 2011-11-8 11:46:22 |显示全部楼层
近期移植一个linux上的代码,该代码采用fwrite把内存中的数据写入临时文件,写成功后再把文件名改为正式的数据文件名,照理应该不会出现什么问题,就算断电写也是写在临时文件的,最多内存中的数据丢失,不至于造成数据库文件的损坏。实际上测试时就是出问题了,发现当电脑突然断电时,写入的数据只有部分,造成了数据文件的损坏。在程序写成功返回后,实际数据还没有真正的完全存入磁盘,改文件名,造成了正式数据库文件的损坏。

现象1:
  1.         fwrite( f_mem_db_buffer->data, 1, f_mem_db_buffer->used, fp );
  2.         fflush(fp);
  3.         fclose( fp );
复制代码
上面这段代码会生成一个f_mem_db_buffer->used大小的文件,但是写入的数据全为0,实际上f_mem_db_buffer->data是有数据的

现象2:
  1.         fwrite( f_mem_db_buffer->data, f_mem_db_buffer->used, 1, fp );
  2.         fflush(fp);
  3.         fclose( fp );
复制代码
上面这段代码会生成一个f_mem_db_buffer->used大小的文件,但是只写入了前部分数据,后部分数据全为0

分析发现造成fwrite的主要原因是写的过程中是分块写入数据的,会把数据拆成N个4096的块进行写入,文件系统会把这种小的写入请求Cache起来,延迟写入。估计是延迟太大,经测试断电时数据库文件损坏几乎是100%。

后改为
  1.         WriteFile(hFile,
  2.                 f_mem_db_buffer->data,
  3.                 f_mem_db_buffer->used,
  4.                 &dwBytesWritten,
  5.                 NULL);
  6.         FlushFileBuffers(hFile);
  7.         CloseHandle(hFile);
复制代码
问题解决,特此MARK一下。
悟空,退下,为师一个人就够了

Rank: 1

发表于 2011-11-8 12:29:23 |显示全部楼层
西裤哥的头像+签名很有喜感~

Rank: 2

发表于 2011-11-8 17:30:54 |显示全部楼层
也可以用CreateFile(...., FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH) 这样就不走缓存

Rank: 1

发表于 2011-11-9 15:45:41 |显示全部楼层
楼上正解

Rank: 1

发表于 2011-11-11 10:24:08 |显示全部楼层
突然断电和fwrite没有写入完全本身是没有冲突的,这是一个很正常、合理的存在。
4想下,你在普通PC下直接把500M的buffer一次性写入,在写入的同时断电,你用什么都要不完整的。
数据为0应该是因为需要保证no_lock写入时的原子操作而做出的预留动作。
断电时候写入不完全的问题放在fwrite是不合理的,你说你要跨平台,又见你用WriteFile,在Linux下总得有个实现吧?小数据的写入你直接用系统调用就行,而非找一个有cache的函数。
再者,你需要保证的是写入数据的正确性,你需要的是添加一个可靠的数据校验,而不是把问题归结到系统接口上。

Rank: 1

发表于 2011-11-12 08:56:51 |显示全部楼层
本帖最后由 sking 于 2011-11-12 09:17 编辑

一个程序的配置文件经常会读写,偶尔会出现配置文件损坏,看来要试试下面这个了

CreateFile(...., FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH) 这样就不走缓存

FILE_FLAG_NO_BUFFERING标志表示不使用缓冲方式,由于磁盘都是以扇区为单位来访问的,在不使用系统磁盘缓冲时,必须自己确保每次读写都是整扇区的。假设每扇区的容量是512字节,那每次WriteFile的长度必须是512的整数倍。

Rank: 1

发表于 2012-1-7 20:59:38 |显示全部楼层
Cyg07 发表于 2011-11-11 10:24
突然断电和fwrite没有写入完全本身是没有冲突的,这是一个很正常、合理的存在。
4想下,你在普通PC下直接把50 ...

你连楼主发的东西都没仔细看,听了半截就开跑,浮躁。
楼主说的是fwrite返回成功,并且fflush,然后改文件名了才断电;而你说的使写500M数据,根本就没返回成功就断电。这算是一回事吗?
没成功返回,就不会执行到改文件名这一步,就不存在文件被损坏,最多发现写入不成功下次继续干。

Rank: 1

发表于 2012-1-9 18:57:46 |显示全部楼层
最近发觉fseek(fp,0,SEEK_END)之后,feof(fp)返回0,真麻烦。

其实Windows里也有io.h里面的_open,_write等POSIX函数来着。
您需要登录后才可以回帖 登录 | 立即加入

Archiver|手机版|第8个男人 - 论坛为只读模式,仅供查阅

GMT+8, 2019-6-27 07:10 , Processed in 0.020645 second(s), 8 queries .

Design by pvo.cn

© 2011 Pvo Inc.

回顶部