稀疏文件(Sparse File), 指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用我们的空间,针对此,WINNT 3.51中的NTFS文件系统对此进行了优化,那些无用的0字节被用一定的算法压缩起来,使得这些0字节不再占用那么多的空间,在你声明一个很大的稀疏文件时(例如 100GB),这个文件实际上并不需要占用这么大的空间,因为里面大都是无用的0数据,那么,NTFS对稀疏文件的压缩算法可以释放这些无用的0字节空间, 可以说这是对磁盘占用空间以及效率的一种优化,记住,FAT32上并不支持稀疏文件的压缩(至少我在自己机子上测试得出如此结论)。
这里,我们将粗略的介绍:
1,如何判断一个磁盘是否支持稀疏文件。
2,如何判断一个文件是否是稀疏文件。
3,如何产生一个稀疏文件。
4,假如系统支持稀疏文件,如何声明这个文件是稀疏文件。
因为这是我工作中出现的问题,所以可能不会深究里面的算法和操作系统机制,但是文章末尾会附上参考资料。
若有问题,可以联系我 [email protected](工作邮箱) 或者 [email protected](私人邮箱)
1.1 判断一个磁盘是否是稀疏文件。
我们可以通过一个系统函数GetVolumeInformation 来判断某个磁盘是否支持稀疏文件的压缩。MSDN中的函数原型如下:
The GetVolumeInformation function retrieves information about a file system and volume that have a specified root directory.
BOOL GetVolumeInformation( LPCTSTR lpRootPathName, LPTSTR lpVolumeNameBuffer, DWORD nVolumeNameSize, LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags, LPTSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize );
我们只要把查询到的Flag 跟 FILE_SUPPORTS_SPARSE_FILES 位与(&),便可以知道该磁盘是否支持。
这是从我的工具集(toolset)里摘录的例子代码:
CHAR szVolName[MAX_PATH], szFsName[MAX_PATH];
DWORD dwSN, dwFSFlag, dwMaxLen, nWritten;
BOOL bSuccess;
HANDLE hFile;
bSuccess = GetVolumeInformation( NULL ,
szVolName,
MAX_PATH,
& dwSN,
& dwMaxLen,
& dwFSFlag,
szFsName,
MAX_PATH);
if (!bSuccess) {
printf( " errno:%d " , GetLastError());
return - 1 ;
}
printf( " vol name:%s /t fs name:%s sn: %d./n " , szVolName, szFsName, dwSN);
if (dwFSFlag & FILE_SUPPORTS_SPARSE_FILES) {
printf( " support sparse file./n " );
} else {
printf( " no support sparse file./n " );
}
2.1 如何判断一个文件是否是稀疏文件。
我们可以通过 GetFileInformationByHandle()函数来判断一个文件是否是稀疏文件。这是MSDN里面的定义。
The GetFileInformationByHandle function retrieves file information for the specified file.
BOOL GetFileInformationByHandle( HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation );例子代码如下:HANDLE hFile;
BY_HANDLE_FILE_INFORMATION stFileInfo;
// Open / create file to get the file handle
hFile = CreateFile();
// Get the file information
GetFileInformationByHandle(hFile, & stFileInfo);
if (stFileInfo.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
{
// Sparse file
} else {
// Not sparse file
}
3.1, 如何产生一个稀疏文件并声明该文件是稀疏文件。
大部分文件,在你改变它的EndOfFile的时候,中间的空白会被操作系统填0,也就是说,如果你用SetFilePointer() 和SetEndOfFile()来
产生一个很大的文件,那么这个文件它占用的是真正的磁盘空间,即使里面全是0,因为系统默认的会在DeviceIoControl()中的ControlCode里用
FSCTL_SET_ZERO_DATA标记,这个标记使得那些文件空洞被0所填充。为了节省磁盘空间,我们必须把一个文件声明为稀疏文件,以便让系统
把那些无用的0字节压缩,并释放相应的磁盘空间,方法如下:
hFile = CreateFile( " tmp_file " ,
GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL ,
CREATE_ALWAYS,
0 ,
NULL );
DWORD dwTemp;
DeviceIoControl(hFile,
FSCTL_SET_SPARSE,
NULL ,
0 ,
NULL ,
0 ,
& dwTemp,
NULL );
SetFilePointer(hFile, 0x100000, NULL , FILE_BEGIN);
WriteFile(hFile,
" 123 " ,
3 ,
& nWritten,
NULL );
SetEndOfFile(hFile);
CloseHandle(hFile);
注意到FSCTL_SET_SPARSE这个标记了吗?正是这个标记,告诉系统该文件是稀疏文件,如果该文件所在的磁盘支持稀疏
文件的压缩,则系统会释放不必要的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>
以上资料参阅了:http://www.flexhex.com/docs/articles/sparse-files.phtml