如何在磁盘上给文件快速预留一大片空间?

gaoteng1984 2008-09-13 08:25:42
比如迅雷下载,下载开始之前会在磁盘上创建一个与下载文件同样大小的空白的原始文件,然后下载过程中去改写这个文件。
我的问题是,在生成这个空白的原始文件时,他是反复调用的WriteFile来写入文件的吗?如果是这样做的,如果文件较大,他无法保证在写这个文件的过程中,系统中有其他进程也向磁盘申请空间,这样还是会造成磁盘碎片问题,该文件的不连续会造成日后对该文件读取的速度降低。
还是他们使用了其他“预留磁盘空间”的API,从而可以真正确保这个原始文件是绝对物理上连续的呢?
...全文
669 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
asight 2011-01-28
  • 打赏
  • 举报
回复
SetFilePointer(hTestFile, fileSize, NULL, FILE_BEGIN);
SetEndOfFile(hTestFile);
CloseHandle(hTestFile);
我申请1.99G,可以看到立即申请完毕,Close*也执行了 程序也关闭了, 但是没有花费时间,这点很奇怪。
我用的是 vc2010 win7 64bits
joyjjjz 2009-06-22
  • 打赏
  • 举报
回复
好帖顶起来
cnzdgs 2008-09-14
  • 打赏
  • 举报
回复
DeviceIoControl是应用程序对设备进行控制的API,各种设备驱动程序都会提供一个设备控制功能接口,用于实现各种控制功能,这方面的东西相当多,以后再逐步了解吧。你可以在MSDN中输入IOCTL_和FSCTL_查看一下常用的控制码的说明。
我没有详细对比过NTFS与FAT文件系统在这方面的差异,不同的文件系统使用的驱动程序不同,所以肯定会有差异的,不过我感觉NTFS也应该要花费同样的时间来执行写操作(稀疏文件除外),可能是在CloseFile的时候才做处理,或者是利用系统线程等其它方式来处理的。
另外提一下,用DeviceIoControl的FSCTL_SET_ZERO_DATA功能是把文件的指定位置添0,SetEndOfFile实际上就是一种设备控制,感觉其内部实现应该是执行了与FSCTL_SET_ZERO_DATA相同的代码。
gaoteng1984 2008-09-14
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 cnzdgs 的回复:]
SetEndOfFile和CreateFileMapping实际上都是向磁盘中写入了0(NTFS的稀疏文件除外),数据是由底层驱动直接写入的,比WriteFile要快一些,使用WriteFile如果代码设计好一些,速度与前两者的差距不大。

我推荐使用CreateFileMapping是因为CreateFileMapping可以先占空间,但不必立即写磁盘操作,在运行过程中由程序控制在合适的时机执行写操作;如果用SetEndOfFile的方式,就变成一开始就要先执行写操作(这个操作是多余的),程序要“等”一段时间后才能继续执行。

要使文件连续,可以通过DeviceIoControl来实现,不过通常情况下文件的碎片很少,对效率的影响是可以忽略的,所以不需要考虑碎片问题。
[/Quote]

王老师果然技艺高强,学生佩服啊,多谢王老师赐教。DeviceIoControl这个API还是第一次看到,貌似功能很强大啊。
我的测试是这样的:
1. CreateFile之后,SetFilePointer移动到1GB位置上,再调SetEndOfFile:
在NTFS分区上,速度巨快,十几毫秒就结束了;在FAT分区上,巨慢,跟使用WriteFile速度几乎一样。
2. SetFilePointer移动到1GB位置上之后,使用WriteFile往文件末尾写入几个字节,再调SetEndOfFile,
在NTFS分区上和FAT分区上,速度都巨慢

我分析:在NTFS分区上,在CreateFile之后,只使用SetEndOfFile来结束文件,系统并没有依次往磁盘里写0,而可能是做了某种标记,标记表明该文件是全0的。而当我在末尾先写上几个字节,再SetEndOfFile时,他就必须要把所有0都真正写到文件里去了。所以前者巨快,后者巨慢。
在FAT分区上,在系统“提前知道”文件是全0时,也要傻乎乎的把所有的0都写到文件里面去,所以怎么才会巨慢。

看来还要了解NTFS和FAT的实现机制,才能弄懂这里。不知道以上分析是否正确。还望王老师不吝赐教。

先结贴给分儿
DentistryDoctor 2008-09-13
  • 打赏
  • 举报
回复
CreateFile/SetFilePointer/SetEndOfFile。
如果要避免碎片, 你必须熟悉NTFS文件系统,直接为文件指责位图(指定磁盘上连接的空间)。
但一般没必要。
zhoujianhei 2008-09-13
  • 打赏
  • 举报
回复
一般都是使用内存映射文件,CreateFile和CreateFileMapping。
xsc2001 2008-09-13
  • 打赏
  • 举报
回复
这个问题就是快速创建一个空文件的问题,先打开一个文件,然后再直接用fseek();移动足够大的空间后,再写上一个字节。再关闭文件就可以了。
猞猁狲 2008-09-13
  • 打赏
  • 举报
回复
学习,学习
cnzdgs 2008-09-13
  • 打赏
  • 举报
回复
SetEndOfFile和CreateFileMapping实际上都是向磁盘中写入了0(NTFS的稀疏文件除外),数据是由底层驱动直接写入的,比WriteFile要快一些,使用WriteFile如果代码设计好一些,速度与前两者的差距不大。

我推荐使用CreateFileMapping是因为CreateFileMapping可以先占空间,但不必立即写磁盘操作,在运行过程中由程序控制在合适的时机执行写操作;如果用SetEndOfFile的方式,就变成一开始就要先执行写操作(这个操作是多余的),程序要“等”一段时间后才能继续执行。

要使文件连续,可以通过DeviceIoControl来实现,不过通常情况下文件的碎片很少,对效率的影响是可以忽略的,所以不需要考虑碎片问题。
用户 昵称 2008-09-13
  • 打赏
  • 举报
回复
噢,连续的就是个问题了。
用户 昵称 2008-09-13
  • 打赏
  • 举报
回复
直接写到文件尾不行吗?
oo_v_oo 2008-09-13
  • 打赏
  • 举报
回复
鼓励LS
gaoteng1984 2008-09-13
  • 打赏
  • 举报
回复
我实际编程测了一下:
使用CreateFile/SetFilePointer/SetEndOfFile,速度很快,支持大于4GB的文件;
使用CreateFileMapping,速度也很快,但是32位程序下只能是小于2GB的文件,而且占用地址空间了,不过后续的“改写”操作,将更加容易进行了;
使用WriteFile,速度相当慢;
fseek的底层应该跟SetFilePointer是一样的。

最后,我是这样分析的:
使用SetFilePointer和CreateFileMapping,都相当于用户向文件系统申请一大块“逻辑上连续”的磁盘空间,因为该申请的速度很快,所以降低了其他进程与此同时申请的几率,从而降低了碎片程度,但是“物理位置”还是由文件系统决定的,只能说文件系统看到用户申请连续空间后,尽量去满足用户的请求以达到优化的目的,但是不一定最终给出真正物理连续的空间。
以上2种方法,都没有实际往磁盘中写数据,只是记录下簿记信息,因此速度巨快。但是用了WriteFile,就要慢慢的写入磁盘了。

如果想得到物理连续的磁盘空间,必须要绕过文件系统,直接以ring0的特权极访问IO端口了吧
cnzdgs 2008-09-13
  • 打赏
  • 举报
回复
CreateFileMapping。
通常不考虑碎片问题。
野男孩 2008-09-13
  • 打赏
  • 举报
回复
CreateFile之后,使用SetFilePointer设置你要的长度的位置,然后SetEndOfFile。

大致是这三个函数。用户态大概没办法保证物理上连续。
yjgx007 2008-09-13
  • 打赏
  • 举报
回复
CreateFileMapping是预留了虚拟地址空间...

15,474

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧