前面提到CreateFileMapping创建映射文件
第三个参数主要有三个值PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY 这三个值得意思我就不在做描述了
其实还有SEC_COMMIT,SEC_IMAGE,SEC_NOCACHE,SEC_RESERVE
我想大家应该知道MEM_COMMIT和MEM_RESERVE标志的意思(VirtualAlloc函数,前面有讲),那么SEC_COMMIT和SEC_RESERVE也是差不多的意思,前者是保留进程空间的地址,后者是将地址提交到物理存储器
如果打个比方,使用SEC_RESRVE就是一个空的容器,容器之中并没有的实际的东西,SEC_COMMIT表示容器是满的
这两个标志,只有在创建由系统的页文件支持的内存映射文件时才有效(存储器来自于页文件中),使用起来颇为不便
那么有没有存储器来自于磁盘文件的类似的用法呢,有(要不然就白讲了)
NT文件系统(NTFS)提供了对稀疏文件的支持,看下什么是NTFS吧
如果是这种文件系统就支持稀疏文件
下面我们讲讲怎么用吧
1.我们要用系统函数检查是否支持稀疏文件
memset(g_Volume, 0, MAX_PATH);
GetCurrentDirectory(MAX_PATH,g_szPath);
strncpy_s(g_Volume, g_szPath, 3);
DWORD dwFileSystemFlags = 0;
bool bOK=GetVolumeInformation(g_Volume, NULL, 0, NULL, NULL, &dwFileSystemFlags, NULL, 0);//获取磁盘信息
bOK = bOK && (dwFileSystemFlags&FILE_SUPPORTS_SPARSE_FILES);//dwFileSystemFlags&FILE_SUPPORTS_SPARSE_FILES查看标志位判断对稀疏文件的支持
2.创建文件,这次使用CreateFile函数(过于简单我就不说了)
3.使用DeviceIoC
DWORD dw;
bool ret = DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL);//FSCTL_SET_SPARSE设置接下来创建的内存映射文件为稀疏文件
4.CreateFileMapping
5.MapViewOfFile得到内存指针,对于稀疏文件,你读每个每个字节内存,它的填充都为0,若是想写入数据,用通常的方法直接对内存赋值即可
使用完了之后,进行一系列的清除操作
这次贴上完整的代码
#include <windows.h>
#include <iostream>
#include <string>
#include <winioctl.h>
using namespace std;
#define FL (1024*1024)
#define FH 0
char g_szPath[MAX_PATH];
char g_Volume[MAX_PATH];
//(3797960)
int main()
{
//先检查当前磁盘是否支持稀疏文件
memset(g_Volume, 0, MAX_PATH);
GetCurrentDirectory(MAX_PATH,g_szPath);
strncpy_s(g_Volume, g_szPath, 3);
DWORD dwFileSystemFlags = 0;
bool bOK=GetVolumeInformation(g_Volume, NULL, 0, NULL, NULL, &dwFileSystemFlags, NULL, 0);//获取磁盘信息
bOK = bOK && (dwFileSystemFlags&FILE_SUPPORTS_SPARSE_FILES);//dwFileSystemFlags&FILE_SUPPORTS_SPARSE_FILES查看标志位判断对稀疏文件的支持
if (!bOK)
{
//如果系统不支持稀疏文件,就直接关闭程序
return 1;
}
DWORD dw;
string FileName = "MMF";
HANDLE hFile = CreateFile(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
int err = GetLastError();
if (hFile == INVALID_HANDLE_VALUE)
{
cout << "打开文件失败" << endl;
return 1;
}
//bool ret=DeviceIoControl(hFile,FSCTL_SET_SPARSE,NULL,0,NULL,0,&dw,NULL);
bool ret = DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL);//设置稀疏文件
err = GetLastError();
if (!ret)
{
//err=GetLastError();
cout << "err no:" << err << endl;
return 2;
}
HANDLE hFileMapping = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, NULL);
if (!hFileMapping)
{
CloseHandle(hFileMapping);
cout << "创建文件映像失败" << endl;
return 2;
}
PBYTE pFile = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
UnmapViewOfFile(pFile);
CloseHandle(hFileMapping);
CloseHandle(hFile);
system("pause");
return 0;
}
创建好的文件时这样子的
虽然可以容纳的字节数为1MB,但是由于并没有分配内存,所以,实际大小只有4KB
我们来看看为什么大小为4KB,而不是0KB
文件在磁盘上所占的空间不是已Byte来衡量 的,而是以簇来衡量的
扇区是磁盘最小的物理存储单元,但由于操作系统无法对数目众多的扇区进行寻址,所以操作系统就将相邻的扇区组合在一起,形成一个簇,然后再对簇进行管理
操作系统规定一个簇中只能放置一个文件的内容,因此文件实际所占用的物理空间大小,只能是簇的整数倍;而如果文件实际大小小于一簇,它也要占一簇的空间
也就是说簇是文件存放的基本单位,那么一簇是多少字节呢
可以看出,这就是占用4KB的原因
另外对于稀疏文件,哪怕你只对其中的一个字节赋值,系统就会提交分配粒度(64KB)大小的区域到存储器