稀疏文件,这是UNIX类和NTFS等文件系统的一个特性。 开始时,一个稀疏文件不包含用户数据,也没有分配到用来存储用户数据的磁盘空间。当数据被写入稀疏文件时,NTFS逐渐地为其分配磁盘空间。一个稀疏文件有可能增长得很大。
稀疏文件以64KB(不同文件系统不同)为单位增量增长,因此磁盘上稀疏文件的大小总是64KB的倍数。 稀疏文件就是在文件中留有很多空余空间,留备将来插入数据使用。如果这些空余空间被ASCII码的NULL字符占据,并且这些空间相当大,那么,这个文件就被称为稀疏文件,而且,并不分配相应的磁盘块。 这样,会产生一个问题,文件已被创建了,但相应的磁盘空间并未被分配,只有在有真正的数据插入进来时,才会被分配磁盘块,如果这时文件系统被占满了,那么对该文件的写操作就会失败。为防止这种情况,有两种办法:不产生稀疏文件或为稀疏文件留够空间。 在计算机科学方面,稀疏文件是文件系统中的一种文件存储方式,在创建一个文件的时候,就预先分配了文件需要的连续存储空间,其空间内部大多都还未被数据填充现在有很多文件系统都支持稀疏文件,包括大部分的Unix和NTFS 。 稀疏文件被普遍用来磁盘图像,数据库快照,日志文件,还有其他科学运用上。
1、WINDOWS中的稀疏文件
稀疏文件(Sparse File), 指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用我们的空间,针对此,WINNT 3.51中的NTFS文件系统对此进行了优化,那些无用的0字节被用一定的算法压缩起来,使得这些0字节不再占用那么多的空间,在你声明一个很大的稀疏文件时(例如 100GB),这个文件实际上并不需要占用这么大的空间,因为里面大都是无用的0数据,那么,NTFS对稀疏文件的压缩算法可以释放这些无用的0字节空间, 可以说这是对磁盘占用空间以及效率的一种优化,记住,FAT32上并不支持稀疏文件的压缩(至少我在自己机子上测试得出如此结论)。
这里,我们将粗略的介绍:
1,如何判断一个磁盘是否支持稀疏文件。
2,如何判断一个文件是否是稀疏文件。
3,如何产生一个稀疏文件。
4,假如系统支持稀疏文件,如何声明这个文件是稀疏文件。
1.1 判断一个磁盘是否是稀疏文件。
我们可以通过一个系统函数GetVolumeInformation 来判断某个磁盘是否支持稀疏文件的压缩。MSDN中的函数原型如下:
GetVolumeInformation
The GetVolumeInformation function retrieves information about a file system and volume that have a specified root directory.
<strong>BOOL GetVolumeInformation( LPCTSTR</strong> <em>lpRootPathName</em><strong>, LPTSTR</strong> <em>lpVolumeNameBuffer</em><strong>, DWORD</strong> <em>nVolumeNameSize</em><strong>, LPDWORD</strong> <em>lpVolumeSerialNumber</em><strong>, LPDWORD</strong> <em>lpMaximumComponentLength</em><strong>, LPDWORD</strong> <em>lpFileSystemFlags</em><strong>, LPTSTR</strong> <em>lpFileSystemNameBuffer</em><strong>, DWORD</strong> <em>nFileSystemNameSize</em><strong> ); </strong> 我们只要把查询到的Flag 跟 FILE_SUPPORTS_SPARSE_FILES 位与(&),便可以知道该磁盘是否支持。 这是从我的工具集(toolset)里摘录的例子代码:
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 927.4625244140625px; font-size: 13px; word-break: break-all; background-color: rgb(238, 238, 238);"><img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> CHAR szVolName[MAX_PATH], szFsName[MAX_PATH]; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> DWORD dwSN, dwFSFlag, dwMaxLen, nWritten; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> BOOL bSuccess; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> HANDLE hFile; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> bSuccess = GetVolumeInformation(NULL, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> szVolName, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> MAX_PATH, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> &dwSN, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> &dwMaxLen, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> &dwFSFlag, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> szFsName, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> MAX_PATH); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> if (!bSuccess) { <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> printf("errno:%d", GetLastError()); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> return -1; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> } <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> printf("vol name:%s \t fs name:%s sn: %d.\n", szVolName, szFsName, dwSN); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> if (dwFSFlag&FILE_SUPPORTS_SPARSE_FILES) { <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> printf("support sparse file.\n"); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> }else{ <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> printf("no support sparse file.\n"); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> }</div>
<span style="color: rgb(204, 0, 0);">2.1 如何判断一个文件是否是稀疏文件。</span> 我们可以通过 GetFileInformationByHandle()函数来判断一个文件是否是稀疏文件。这是MSDN里面的定义。 <p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px;"> </p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px;">The <strong>GetFileInformationByHandle</strong> function retrieves file information for the specified file.</p> <p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px;"> </p> <pre class="syntax" name="code" style="white-space: pre-wrap; word-wrap: break-word;"><strong>BOOL GetFileInformationByHandle( HANDLE</strong> <em>hFile</em><strong>, LPBY_HANDLE_FILE_INFORMATION</strong> <em>lpFileInformation</em><strong> );</strong>
例子代码如下:
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 927.4625244140625px; font-size: 13px; word-break: break-all; background-color: rgb(238, 238, 238);"><img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />HANDLE hFile; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />BY_HANDLE_FILE_INFORMATION stFileInfo; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />//Open/create file to get the file handle <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />hFile = CreateFile(<img src="http://www.cppblog.com/Images/dot.gif" alt="" style="border: none; max-width: 100%;" />); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />//Get the file information <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />GetFileInformationByHandle(hFile, &stFileInfo); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />if(stFileInfo.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />{ <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> //Sparse file <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />}else{ <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> //Not sparse file <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" />}</div>
<span style="color: rgb(204, 0, 0);">3.1, 如何产生一个稀疏文件并声明该文件是稀疏文件。</span>
大部分文件,在你改变它的EndOfFile的时候,中间的空白会被操作系统填0,也就是说,如果你用SetFilePointer() 和SetEndOfFile()来
产生一个很大的文件,那么这个文件它占用的是真正的磁盘空间,即使里面全是0,因为系统默认的会在DeviceIoControl()中的ControlCode里用
<strong>FSCTL_SET_ZERO_DATA</strong>标记,这个标记使得那些文件空洞被0所填充。为了节省磁盘空间,我们必须把一个文件声明为稀疏文件,以便让系统
把那些无用的0字节压缩,并释放相应的磁盘空间,方法如下:
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 927.4625244140625px; font-size: 13px; word-break: break-all; background-color: rgb(238, 238, 238);"><img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> hFile = CreateFile("tmp_file", <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> GENERIC_WRITE|GENERIC_READ, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> FILE_SHARE_READ|FILE_SHARE_WRITE, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> CREATE_ALWAYS, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> 0, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> DWORD dwTemp; <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> DeviceIoControl(hFile, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> FSCTL_SET_SPARSE, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> 0, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> 0, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> &dwTemp, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> SetFilePointer(hFile, 0x100000, NULL, FILE_BEGIN); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> WriteFile(hFile, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> "123", <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> 3, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> &nWritten, <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> NULL); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> SetEndOfFile(hFile); <img align="top" src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" alt="" style="border: none; max-width: 100%;" /> CloseHandle(hFile);</div>
注意到<strong>FSCTL_SET_SPARSE</strong>这个标记了吗?正是这个标记,告诉系统该文件是稀疏文件,如果该文件所在的磁盘支持稀疏 文件的压缩,则系统会释放不必要的0字节空间。你可以用这个方法创建一个100GB得文件试一下(示例里是1M),记得右键看看文件属性 里的‘大小’和占用空间,它被声明为100GB,但是实际上那些0字节基本不占用空间,而你写入的“123”是占用实际的 磁盘空间的。
注意:在FAT32得磁盘里,因为没有对SPARSE FILE得支持,所以您创建的空洞文件全部被填零,即使你声明它是一个稀疏
文件,也没有任何作用,您声明这个文件多大,它就占用多大的空间。
另外,如果您编译 DeviceIoControl这个函数出现 "'FSCTL_SET_SPARSE' : undeclared identifier"之类的情况
请这样做:
#include <windows.h> #define _WIN32_WINNT 0x0501 #include <Winioctl.h>
2、Linux文件空洞与稀疏文件
在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞。位于文件中但没有写过的字节都被设为0。
如果offset 比文件的当前长度更大,下一个写操作就会把文件“撑大(extend)” 在文件里创造“空洞(hole)”。没有被实际写入文件的所有字节由重复的0 表示。空洞是否占用硬盘空间是由文件系统(file system)决定的。
![](https://img-my.csdn.net/uploads/201304/04/1365086751_7878.jpg)
稀疏文件(Sparse File)
稀疏文件与其他普通文件基本相同,区别在于文件中的部分数据是全0,且这部分数据不占用磁盘空间。
下面是稀疏文件的创建与查看
[root@localhost ~]# dd if=/dev/zero of=sparse-file bs=1 count=1 seek=1024k
[root@localhost ~]# ls -l sparse-file
-rw-r--r-- 1 root root 1048577 Oct 15 17:50 sparse-file
[root@localhost ~]# du -sh sparse-file
8.0K sparse-file
[root@localhost ~]# cat anaconda-ks.cfg >> sparse-file
[root@localhost ~]# du -sh sparse-file
12Ksparse-file
[root@localhost ~]# du -sh anaconda-ks.cfg
12Kanaconda-ks.cfg
[root@localhost ~]#
稀疏文件与其他普通文件基本相同,区别在于文件中的部分数据是全0,且这部分数据不占用磁盘空间。
下面是稀疏文件的创建与查看
[root@localhost ~]# dd if=/dev/zero of=sparse-file bs=1 count=1 seek=1024k
[root@localhost ~]# ls -l sparse-file
-rw-r--r-- 1 root root 1048577 Oct 15 17:50 sparse-file
[root@localhost ~]# du -sh sparse-file
8.0K sparse-file
[root@localhost ~]# cat anaconda-ks.cfg >> sparse-file
[root@localhost ~]# du -sh sparse-file
12Ksparse-file
[root@localhost ~]# du -sh anaconda-ks.cfg
12Kanaconda-ks.cfg
[root@localhost ~]#
文件系统存储稀疏文件时,inode索引节点中,只给出实际占用磁盘空间的block号,数据全0,且不占用磁盘空间的文件block并没有物理磁盘block号。
![](https://img-my.csdn.net/uploads/201304/04/1365087967_4380.jpg)
linux稀疏文件Inode数据块存储:
文件空洞部分不占用磁盘空间
文件所占用的磁盘空间仍然是连续的
![](https://img-my.csdn.net/uploads/201304/04/1365090072_2150.jpg)
![](https://img-my.csdn.net/uploads/201304/04/1365090240_4962.jpg)