稀疏提交的内存映射文件

前面提到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)大小的区域到存储器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章