第17章 内存映射文件(3)_稀疏文件(Sparse File)

17.8 稀疏调拨的内存映射文件

17.8.1 稀疏文件简介

(1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间。NTFS文件系统对此进行了优化,那些无用的0字节被用一定的算法压缩起来。例如声明一个很大的稀疏文件(如100GB),这个文件实际上并不需要占用那么大的空,内部都是一些无用的0数据,那么NTFS就会利用算法释放这些无用的0字节空间,这是对磁盘占用空间的一种优化。但要注意FAT32并不支持稀疏文件的压缩

(2)与稀疏文件操作有关的函数

  ①判断系统是否支持稀疏文件:GetVolumeInformation,通过传出的参数lpFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES判断结果是否为FILE_SUPPORTS_SPARSE_FILES。

  ②判断一个文件是否是稀疏文件:GetFileInformationByHandle

    BY_HANDLE_FILE_INFORMATION stFileInfo;

    GetFileInformationByHandle(hFile, &stFileInfo);

    当stFileInfo.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE为TRUE时表示稀疏文件。

  ③产生一个稀疏文件:DeviceIoControl(hFile,FSCTL_SET_SPARSE,…);

  大部分文件,在改变它的EndOfFile的时候,中间的空白会被操作系统填0,也就是说,如果用SetFilePointer和SetEndOfFile来产生一个很大的文件,那么这个文件它占用的是真正的磁盘空间,即使里面全是0,系统默认的也会在DeviceIoControl()中的ControlCode里用FSCTL_SET_ZERO_DATA标记,这个标记使得那些文件空洞被0所填充。为了节省磁盘空间,我们必须把一个文件声明为稀疏文件,以便让系统把那些无用的0字节压缩,并释放相应的磁盘空间,要将标记改为FSCTL_SET_SPARSE。

  ④查找稀疏文件中包含非零数据的范围

    DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,…);

17.8.2 以稀疏文件为后备存储器的内存映射文件(一般的使用步骤)

(1)创建文件:hFile = CreateFile(…);

(2)产生稀疏文件:

  DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL));

(3)创建内存映射文件对象:

  hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE,…);

(4)映射到进程的地址空间

pvFile = MapViewOfFile(hFileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);

(5)读、写文件(像直接读写内存一样的读写文件)

(6)撤消映射:UnmapViewOfFile(pvFile);

(7)关闭句柄:CloseHandle(hFileMap);CloseHandle(hFile);

【MMFSparse程序】创建一个NTFS稀疏文件为后备存储器的内存映射文件

//MMFSparse.cpp

/************************************************************************
Module:  MMFSparse.cpp
Notices: Copyright(c) 2008 Jeffrey Ritcher & Christophe Nasarre
************************************************************************/
#include "../../CommonFiles/CmnHdr.h"
#include "SparseStream.h"
#include "resource.h"
#include <tchar.h>
#include <strsafe.h>

//////////////////////////////////////////////////////////////////////////
//定义一个稀疏文件的内存映射文件操作类
class CMMFSparse :public CSparseStream{
private:
    HANDLE m_hFileMap;  //文件映射对象
    PVOID  m_pvFile;    //视图开始的地址

public:
    //构造函数(hStream为外部传进来的文件对象的句柄)
    CMMFSparse(HANDLE hStream = NULL, DWORD dwStreamSizeMaxLow = 0,
               DWORD dwStreamSizeMaxHigh = 0){
        Initialize(hStream, dwStreamSizeMaxLow, dwStreamSizeMaxHigh);
    }

    //关闭Sparse MMF
    virtual ~CMMFSparse(){ ForceClose(); }

    //创建一个以稀疏文件为后备存储器的MMF对象,并映射到进程的地址空间
    BOOL Initialize(HANDLE hStream = NULL, DWORD dwStreamSizeMaxLow = 0,
                    DWORD dwStreamSizeMaxHigh = 0);

    //将CMMFSparse类的对象指针,转化为视图的第1个字节的地址
    operator PBYTE() const { return (PBYTE)m_pvFile;}

    //允许显式的关闭MMF,而不需要等待析构函数的执行
    VOID ForceClose();
};

//////////////////////////////////////////////////////////////////////////
BOOL CMMFSparse::Initialize(HANDLE hStream, DWORD dwStreamSizeMaxLow, DWORD dwStreamSizeMaxHigh){
    if (m_hFileMap != NULL)
        ForceClose();

    //初始化
    m_hFileMap = m_pvFile = NULL;

    BOOL bOk = TRUE; //假设是成功的

    if (hStream != NULL){
        if ((dwStreamSizeMaxLow == 0) && (dwStreamSizeMaxHigh ==0)){
            DebugBreak(); //非法的参数
        }

        CSparseStream::Initialize(hStream);
        bOk = MakeSparse();  //产生一个稀疏文件
        if (bOk){
            //创建一个内存文件映射对象
            m_hFileMap = ::CreateFileMapping(hStream, NULL, PAGE_READWRITE,
                                             dwStreamSizeMaxHigh,dwStreamSizeMaxLow,NULL);
            if (m_hFileMap != NULL){
                //将m_hFileMap映射到进程的地址空间,形成一个视图
                m_pvFile = ::MapViewOfFile(m_hFileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
            } else{
                //映射失败时
                CSparseStream::Initialize(NULL);
                ForceClose();
                bOk = FALSE;
            }
        }
    }
    return (bOk);
}

//////////////////////////////////////////////////////////////////////////
VOID CMMFSparse::ForceClose(){
    //清理
    if (m_pvFile != NULL){
        ::UnmapViewOfFile(m_pvFile); //撤消映射
        m_pvFile = NULL;
    }

    if (m_hFileMap != NULL){
        ::CloseHandle(m_hFileMap);
        m_hFileMap = NULL;
    }
}

//////////////////////////////////////////////////////////////////////////
TCHAR g_szPathname[MAX_PATH] = TEXT("\0");
HANDLE g_hStream = INVALID_HANDLE_VALUE;
CMMFSparse g_mmf;
#define STREAMSIZE   (1*1024*1024) //1MB (1024 KB)

//////////////////////////////////////////////////////////////////////////
void Dlg_ShowAllocatedRanges(HWND hwnd){
    //内存映射文件有实际的后备储存器的情况显示在“己分配范围”编辑框中
    DWORD dwNumEntries;
    FILE_ALLOCATED_RANGE_BUFFER* pfarb =
        g_mmf.QueryAllocateRanges(&dwNumEntries);
    if (dwNumEntries ==0){
        SetDlgItemText(hwnd, IDC_FILESTATUS, TEXT("文件中没有己分配的范围"));
    } else{
        TCHAR sz[4096] = { 0 };
        for (DWORD dwEntry = 0; dwEntry < dwNumEntries;dwEntry++)
            StringCchPrintf(_tcschr(sz, _T(‘\0‘)),
                            _countof(sz) - _tcslen(sz),
                            TEXT("偏移:0x%05X,长度:%u KB\r\n"),
                            //TEXT("偏移:%7.7u,长度:%7.7u\r\n"),
                            pfarb[dwEntry].FileOffset.LowPart,
                            pfarb[dwEntry].Length.LowPart/1024);
        SetDlgItemText(hwnd, IDC_FILESTATUS, sz);
    }

    g_mmf.FreeAllocateRanges(pfarb);
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){
    chSETDLGICONS(hwnd, IDI_MMFSPARSE);

    //初始化对话框上的各控件
    EnableWindow(GetDlgItem(hwnd, IDC_OFFSET), FALSE);
    Edit_LimitText(GetDlgItem(hwnd, IDC_OFFSET), 4);
    SetDlgItemInt(hwnd, IDC_OFFSET, 1000, FALSE);

    EnableWindow(GetDlgItem(hwnd, IDC_BYTE), FALSE);
    Edit_LimitText(GetDlgItem(hwnd, IDC_BYTE),3);
    SetDlgItemInt(hwnd, IDC_BYTE, 5, FALSE);

    EnableWindow(GetDlgItem(hwnd, IDC_WRITEBYTE), FALSE);
    EnableWindow(GetDlgItem(hwnd, IDC_READBYTE), FALSE);
    EnableWindow(GetDlgItem(hwnd, IDC_FREEALLOCATEDREGIONS), FALSE);

    //将文件保存在一个可写的文件夹中
    GetCurrentDirectory(_countof(g_szPathname), g_szPathname);
    _tcscat_s(g_szPathname, _countof(g_szPathname), TEXT("\\MMFSparse"));

    //检查当前驱动器是否支持稀疏文件
    TCHAR szVolume[16];
    PTSTR pEndOfVolume = _tcschr(g_szPathname, _T(‘\\‘)); //查找驱动器号
    if (pEndOfVolume == NULL){
        chFAIL("无法在默认的文件夹中找到驱动器的卷标");
        DestroyWindow(hwnd);
        return TRUE;
    }

    _tcsncpy_s(szVolume, _countof(szVolume),
               g_szPathname,pEndOfVolume-g_szPathname +1); //含‘\‘,形如:C:\

    if (!CSparseStream::DoesFileSystemSupportSparseStreams(szVolume)){
        chFAIL("默认的文件夹所在驱动器不支持稀疏文件!");
        DestroyWindow(hwnd);
        return TRUE;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity){
    switch (id)
    {
    case IDCANCEL:
        EndDialog(hwnd, id);
        break;

    case IDC_CREATEMMF:
        {
            g_hStream = CreateFile(g_szPathname, GENERIC_READ | GENERIC_WRITE,
                                   0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (g_hStream == INVALID_HANDLE_VALUE){
                chFAIL("创建文件失败!");
                return;
            }

            //创建一个1MB(1024KB)的MMF
            if (!g_mmf.Initialize(g_hStream,STREAMSIZE,0)){
                chFAIL("初始化稀疏内存映射文件失败!");
                CloseHandle(g_hStream);
                g_hStream = NULL;
                return;
            }

            //显示分配情况
            Dlg_ShowAllocatedRanges(hwnd);

            //启用或禁用相关控件
            EnableWindow(GetDlgItem(hwnd, IDC_CREATEMMF), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_OFFSET),        TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_BYTE),        TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_WRITEBYTE),    TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_READBYTE),    TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_FREEALLOCATEDREGIONS), TRUE);

            //将焦点设置在“偏移”编辑框
            SetFocus(GetDlgItem(hwnd, IDC_OFFSET));
        }
        break;

    case IDC_READBYTE:
        {
            BOOL bTranslated;
            DWORD dwOffset = GetDlgItemInt(hwnd, IDC_OFFSET,
                                           &bTranslated,//转换是否成功
                                           FALSE);//无符号数
            if (bTranslated){
                SetDlgItemInt(hwnd, IDC_BYTE, g_mmf[dwOffset * 1024], FALSE);
                Dlg_ShowAllocatedRanges(hwnd);
            }
        }
        break;

    case IDC_WRITEBYTE:
        {
            BOOL bTranslated;
            DWORD dwOffset = GetDlgItemInt(hwnd, IDC_OFFSET,
                                           &bTranslated,//转换是否成功
                                           FALSE);//无符号数
            if (bTranslated){
                g_mmf[dwOffset * 1024] = (BYTE)
                       GetDlgItemInt(hwnd,IDC_BYTE,NULL,FALSE);
                Dlg_ShowAllocatedRanges(hwnd);
            }
        }
        break;

    case IDC_FREEALLOCATEDREGIONS:
        {
            //通常,析构函数会关闭内存映射文件。
            //但这里,我们是按“释放”按钮来强制关闭的,以便重置文件的一些区域为0
            g_mmf.ForceClose();

            //我们可以在上面调用ForceClose函数是因为当试图将一个己映射的
            //文件的部分区域置0时,会引起DeviceIoControl调用失败。
            g_mmf.DecommitPortionOfStream(0, STREAMSIZE);//文件所有字节都置0

            //要关闭文件句柄并重新打弄,以便将更改刷新稀疏文件的状态
            CloseHandle(g_hStream);

            //重新创建新文件
            g_hStream = CreateFile(g_szPathname, GENERIC_READ | GENERIC_WRITE,
                                   0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (g_hStream == INVALID_HANDLE_VALUE){
                chFAIL("创建文件失败!");
                return;
            }

            //重置为稀疏文件
            g_mmf.Initialize(g_hStream, STREAMSIZE, 0);

            //更新界面
            Dlg_ShowAllocatedRanges(hwnd);
        }
        break;
    }
}

//////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
    switch (uMsg)
    {
        chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
        chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
    }
    return FALSE;
}
//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int){
    DialogBox(hInstExe, MAKEINTRESOURCE(IDD_MMFSPARSE), NULL, Dlg_Proc);
    return 0;
}

//SparseStream.h

/************************************************************************
Module: SparseStream.h
Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#include "../../CommonFiles/CmnHdr.h"
//#include <WinIoCtl.h> //For FILE_ALLOCATED_RANGE_BUFFER*
#pragma once
//////////////////////////////////////////////////////////////////////////

class CSparseStream{
public:
    //判断某个驱动器是否支持稀疏文件
    static BOOL DoesFileSystemSupportSparseStreams(PCTSTR pszVolume);
    //判断一个文件是否是稀疏文件(静态方法)
    static BOOL DoesFileContainAnySparseStreams(PCTSTR pszPathname);

public:
    CSparseStream(HANDLE hStream = INVALID_HANDLE_VALUE){
        Initialize(hStream);
    }

    virtual ~CSparseStream(){}

    void Initialize(HANDLE hStream = INVALID_HANDLE_VALUE){
        m_hStream = hStream;
    }
public:
    BOOL IsStreamSparse() const; //是否是稀疏文件(非静态方法)
    BOOL MakeSparse(); //产生稀疏文件

    //给稀疏文件指定范围的内容填0
    BOOL DecommitPortionOfStream(__int64 qwFileOffsetStart, __int64 qwFileOffsetEnd);

    //查找稀疏文件中非零数据的范围
    FILE_ALLOCATED_RANGE_BUFFER* QueryAllocateRanges(PDWORD pdwNumEntries);

    //释放QueryAllocateRanges分配到的内存空间
    BOOL FreeAllocateRanges(FILE_ALLOCATED_RANGE_BUFFER* pfarb);

public:
    operator HANDLE() const{ return (m_hStream);}
private:
    HANDLE m_hStream;

private:
    static BOOL AreFlagsSet(DWORD fdwFlagBits, DWORD fFlagsToCheck){
        return ((fdwFlagBits & fFlagsToCheck) == fFlagsToCheck);
    }
};

//////////////////////////////////////////////////////////////////////////
inline BOOL CSparseStream::DoesFileSystemSupportSparseStreams(PCTSTR pszVolume){
    DWORD dwFileSystemFlags = 0;
    BOOL bOk = GetVolumeInformation(pszVolume, NULL, 0, NULL, NULL,
                                    &dwFileSystemFlags, NULL, 0);
    bOk = bOk&& AreFlagsSet(dwFileSystemFlags, FILE_SUPPORTS_SPARSE_FILES);
    return bOk;
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CSparseStream::DoesFileContainAnySparseStreams(PCTSTR pszPathname){
    DWORD dw = GetFileAttributes(pszPathname);
    return ((dw == 0xFFFFFFFF) ?
             FALSE:
             AreFlagsSet(dw,FILE_ATTRIBUTE_SPARSE_FILE));
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CSparseStream::IsStreamSparse() const{
    BY_HANDLE_FILE_INFORMATION bhfi;
    GetFileInformationByHandle(m_hStream, &bhfi);

    return (AreFlagsSet(bhfi.dwFileAttributes, FILE_ATTRIBUTE_SPARSE_FILE));
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CSparseStream::MakeSparse(){
    DWORD dw;
    return (DeviceIoControl(m_hStream, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL));
}

//////////////////////////////////////////////////////////////////////////
//给文件指定范围的内容填0.如果文件是稀疏文件,NTFS会释放文件,并不会扩大文件的实际大小
inline BOOL CSparseStream::DecommitPortionOfStream(__int64 qwFileOffsetStart, __int64 qwFileOffsetEnd){
    //注意:如果文件尚未被映射时,该函数将不能正常工作
    DWORD dw;
    FILE_ZERO_DATA_INFORMATION fzdi;
    fzdi.FileOffset.QuadPart = qwFileOffsetStart;
    fzdi.BeyondFinalZero.QuadPart = qwFileOffsetEnd + 1;
    return (DeviceIoControl(
        m_hStream, //文件句柄
        FSCTL_SET_ZERO_DATA,//控制码
        (PVOID)&fzdi, //input buffer,指明了文件要设置为0的范围
        sizeof(fzdi), //缓冲区大小
        NULL, //lpOutBuffer
        0,    //nOutBufferSize
        &dw, //number of bytes returned
        NULL));
}

//////////////////////////////////////////////////////////////////////////
//查找稀疏文件中包含非零数据的范围
FILE_ALLOCATED_RANGE_BUFFER* CSparseStream::QueryAllocateRanges(PDWORD pdwNumEntries){
    FILE_ALLOCATED_RANGE_BUFFER farb;
    farb.FileOffset.QuadPart = 0;
    farb.Length.LowPart = GetFileSize(m_hStream, (PDWORD)&farb.Length.HighPart);

    //试图收集数据之前,我们没有办法切确地知道内存块的大小,这里我们选择100*sizeof(farb)
    DWORD cb = 100 * sizeof(farb);
    FILE_ALLOCATED_RANGE_BUFFER* pfarb = (FILE_ALLOCATED_RANGE_BUFFER*)
        HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);

    DeviceIoControl(m_hStream, FSCTL_QUERY_ALLOCATED_RANGES,
                    &farb,sizeof(farb),//输入缓冲区
                    pfarb,cb,&cb, //输出缓冲区
                    NULL);
    *pdwNumEntries = cb / sizeof(*pfarb);
    return (pfarb);
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CSparseStream::FreeAllocateRanges(FILE_ALLOCATED_RANGE_BUFFER* pfarb){
    return (HeapFree(GetProcessHeap(), 0, pfarb));
}

/////////////////////////////文件结束/////////////////////////////////////

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 17_MMFSparse.rc 使用
//
#define IDD_MMFSPARSE                   1
#define IDC_CREATEMMF                   101
#define IDI_MMFSPARSE                   102
#define IDC_OFFSET                      103
#define IDC_WRITEBYTE                   105
#define IDC_READBYTE                    106
#define IDC_BYTE                        109
#define IDC_FILESTATUS                  1000
#define IDC_FREEALLOCATEDREGIONS        1002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//MMFSparse.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_MMFSPARSE           ICON                    "MMFSparse.ico"

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_MMFSPARSE DIALOGEX 15, 24, 188, 192
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "MMF Sparse"
FONT 10, "宋体", 400, 0, 0x0
BEGIN
    DEFPUSHBUTTON   "创建一个1M(1024KB)的稀疏内存映射文件对象",IDC_CREATEMMF,10,3,168,14,WS_GROUP
    LTEXT           "偏移(0 - 1023KB):",IDC_STATIC,10,23,68,8
    EDITTEXT        IDC_OFFSET,81,21,44,12
    PUSHBUTTON      "读取字节",IDC_READBYTE,132,19,46,14
    LTEXT           "字节(0-255):",IDC_STATIC,11,38,47,8
    EDITTEXT        IDC_BYTE,81,36,44,12,ES_UPPERCASE | ES_NUMBER
    PUSHBUTTON      "写入字节",IDC_WRITEBYTE,132,35,46,14
    PUSHBUTTON      "释放所有己分配的区域",IDC_FREEALLOCATEDREGIONS,80,51,98,14
    LTEXT           "分配范围:",IDC_STATIC,9,63,59,8
    EDITTEXT        IDC_FILESTATUS,9,72,170,116,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
END

/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_MMFSPARSE, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 184
        TOPMARGIN, 1
        BOTTOMMARGIN, 190
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

17.8.3 内存映射文件的其他问题

(1)CreateFileMapping中fdwProtect参数的讨论

  ①SEC_COMMIT:只有当以页交换文件为后备存储器时才有起作用,用于给区域调拨物理存储器。不指定该标志时,默认也是以SEC_COMMIT创建内存映射文件的。(SEC,Section的缩写)。

  ②SEC_RESERVE:也是以页交换文件为后备存储器,但系统不会从页交换文件中调拨物理存储器,只返回文件映射对象的句柄。

(2)利用SEC_RESERVE进行部分区域的调拨

  ①默认下CreateFileMapping映射文件时,会传入SEC_COMMIT标志,所以MapViewOfFile会预订一块地址空间区域,并给区域(大小由该函数的dwNumberOfBytesToMap参数指定)全部调拨物理存储器,这有点浪费。我们可以在CreateFileMapping创建时传入SEC_RESERVE,用于告诉MapViewOfFile只预订这个区域,而不调拨物理存储器!

  ②区域的部分调拨的具体步骤:

    A、CreateFileMapping时传入SEC_RESERVE。

    B、创建视图:pvAddress = MapViewOfFile,这时会得到一个区域,但只是预订而不会给区域调拨物理存储器。

    C、调用VirtualAlloc给区域调拨物理存储器,传上面的pvAddress传入该函数

    D、这里可以像一般的内存映射文件一样的使用了。

  ③注意事项:

    A、通过SEC_RESERVE标志得到的内存映射文件,不能用VirtualFree来撤消调拨的物理存储器,仍然要用UnmapViewOfFile。

    B、SEC_RESERVE和SEC_COMMIT标志只能用在以“页交换”文件为后备存储器的内存映射文件。

时间: 2024-10-11 07:14:18

第17章 内存映射文件(3)_稀疏文件(Sparse File)的相关文章

Linux文件空洞与稀疏文件 转

1.Linux文件空洞与稀疏文件 2.文件系统数据存储 3.文件系统调试 文件空洞 在UNIX文件操作中,文件位移量可以大于文件的当前长度在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞.位于文件中但没有写过的字节都被设为 0. 如果 offset 比文件的当前长度更大,下一个写操作就会把文件“撑大(extend)”在文件里创造“空洞(hole)”.没有被实际写入文件的所有字节由重复的 0 表示.空洞是否占用硬盘空间是由文件系统(file system)决定的 稀疏文件(Sp

《Linux Device Drivers》第十五章 内存映射和DMA——note

简单介绍 很多类型的驱动程序编程都须要了解一些虚拟内存子系统怎样工作的知识 当遇到更为复杂.性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现过程 讲述怎样跨越边界直接訪问用户空间的内存页 讲述了直接内存訪问(DMA)I/O操作,它使得外设具有直接訪问系统内存的能力 Linux的内存管理 地址类型 Linux是一个虚拟内存系统,这意味着用户程序所使用的地址与硬件使用的物理地址是不等同的 有了虚拟内存,在系统中执行的程序能够分配比物理内存很

python文件操作_对文件进行复制拷贝_代码实现

要求: 1,对已经存在的文件进行复制操作 2,复制后的文件在文件名后面加上[复件] 3,文件比较大如何优化处理 1 #-*- coding: UTF-8 -*- 2 #这是python 2 下面写的,用的raw_input 3 #old_file_name用来接收输入的文件名 4 old_file_name = raw_input('请输入要复制的文件名:') 5 #打开输入的文件 6 old_file = open(old_file_name,'r') 7 8 #对新文件的名字进行处理,在后缀

67.文件映射为内存进行操作与多线程 以及 文件映射到内存根据索引进行内存来实现读取多线程操作

问题引出: //有限的额内存情况怎么干活//索引载入内存,多线程//索引映射为内存 一.载入内存,按行读取进行多线程 定义路径以及其他信息 1 char path[256] = "kaifangX.txt"; 2 char indexpath[256] = "indexkf.txt"; 3 #define N 20151574 创建索引 1 struct index 2 { 3 int *pindex;//地址 4 int length; 5 }allindex =

NIO之内存映射文件原理

原文链接:https://www.cnblogs.com/lyftest/p/6564547.html Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原理. 在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数  read().write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然

4.关于QT中的QFile文件操作,QBuffer,Label上添加QPixmap,QByteArray和QString之间的区别,QTextStream和QDataStream的区别,QT内存映射(

 新建项目13IO 13IO.pro HEADERS += \ MyWidget.h SOURCES += \ MyWidget.cpp QT += gui widgets network CONFIG += C++11 MyWidget.h #ifndef MYWIDGET_H #define MYWIDGET_H   #include <QWidget>   class MyWidget : public QWidget {     Q_OBJECT public:     expli

Java NIO内存映射---上G大文件处理

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了java中内存映射的原理及过程,与传统IO进行了对比,最后,用实例说明了结果. 一.java中的内存映射IO和内存映射文件是什么? 内存映射文件非常特别,它允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快.加载内存映射文件所使用的内存在Java堆区之外.Java编程语言

内存映射文件处理大文件

先说结论:使用内存映射文件来处理大文件可以提高效率. 为什么呢? 我们先来看看如果不使用内存映射文件的处理流程是怎样的,首先我们得先读出磁盘文件的内容到内存中,然后修改,最后回写到磁盘上.第一步读磁盘文件是要经过一次系统调用的,它首先将文件内容从磁盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,实际上是两次数据拷贝.第三步回写也一样也要经过两次数据拷贝. 所以我们基本上会有四次数据的拷贝了,因为大文件数据量很大,几十GB甚至更大,所以拷贝的开销是非常大的. 而内存映射文件是操作系统

Java NIO内存映射---上G大文件处理(转)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了java中内存映射的原理及过程,与传统IO进行了对比,最后,用实例说明了结果. 一.java中的内存映射IO和内存映射文件是什么? 内存映射文件非常特别,它允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快.加载内存映射文件所使用的内存在Java堆区之外.Java编程语言