稀疏提交的內存映射文件

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

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