首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 使用記憶體對應檔案處理超大檔案(GB/TB 級)

     編輯:VC 教學

前言

傳統 ReadFile/WriteFile 或 MFC CFile 在處理數十 GB 甚至 TB 級檔案時,頻繁的 I/O 呼叫、緩衝區配置與資料拷貝會造成嚴重效能瓶頸。Windows 記憶體對應檔案 將檔案直接映射至進程虛擬位址空間,讓 CPU 以指標方式存取檔案內容,所有分頁管理、預讀、寫回由系統核心自動完成,極大降低開發複雜度並提升吞吐量。本文詳細說明核心 API 流程、關鍵參數設定,並提供一個接收高速資料流並即時寫入磁碟的實戰範例。

核心 API 流程總覽

  1. CreateFile:建立/開啟檔案核心物件,取得 HANDLE。
  2. CreateFileMapping:建立檔案映射物件,指定保護屬性(PAGE_READWRITE 等)與最大尺寸(64-bit,支援 16 EB)。
  3. MapViewOfFile:將映射物件的全部或部分映射至進程位址空間,傳回基底指標 LPVOID。
  4. 直接對記憶體指標讀寫資料(memcpy、指標運算等)。
  5. UnmapViewOfFile:解除映射視圖。
  6. CloseHandle:關閉映射物件與檔案物件。

關鍵參數與注意事項

API關鍵參數說明
CreateFiledwDesiredAccess, dwShareMode需與後續映射保護屬性一致;FILE_FLAG_SEQUENTIAL_SCAN 可提示系統做循序預讀。
CreateFileMappingflProtect, dwMaximumSizeHigh/LowPAGE_READWRITE 需搭配 GENERIC_READ|GENERIC_WRITE;尺寸以 64-bit 指定。
MapViewOfFiledwFileOffsetHigh/Low, dwNumberOfBytesToMap偏移量必須為系統配置粒度(通常 64 KB)整數倍;可分批映射大檔案的不同區段。

實戰範例:高速資料流即時落盤

情境:從硬體埠接收資料,總量約數十 GB,需即時寫入磁碟,且最終檔案大小需精確反映實際接收量(而非預配大小)。

// ----- 初始化:預配 64 MB 映射空間 -----
HANDLE hFile = CreateFile(L"Recv.tmp",
    GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
    CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE,
    0, 0x4000000, NULL);          // 64 MB
CloseHandle(hFile);                // 檔案控制代碼可先關閉

SYSTEM_INFO si; GetSystemInfo(&si);
const DWORD granularity = si.dwAllocationGranularity; // 通常 64 KB
const DWORD viewBytes = 1000 * granularity;           // 每次映射 ~64 MB

__int64 fileOffset = 0;       // 檔案邏輯位移
__int64 nextRemapThreshold = 600 * granularity;       // 38 MB 觸發重映射
BYTE* pView = (BYTE*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,
    0, 0, viewBytes);

// ----- 接收迴圈 -----
while (running)
{
    DWORD idx = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
    if (idx == WAIT_OBJECT_0)         // 資料到達事件
    {
        int nRead = syio_Read(port, pView + fileOffset, queueLen);
        fileOffset += nRead;

        // 當寫入量超過閾值,重映射下一區塊
        if (fileOffset > nextRemapThreshold)
        {
            UnmapViewOfFile(pView);
            DWORD offsetLow  = (DWORD)(fileOffset & 0xFFFFFFFF);
            DWORD offsetHigh = (DWORD)(fileOffset >> 32);
            pView = (BYTE*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,
                offsetHigh, offsetLow, viewBytes);
            nextRemapThreshold = fileOffset + 600 * granularity;
        }
    }
    else if (idx == WAIT_OBJECT_0 + 1) // 結束事件
    {
        break;
    }
}

// ----- 收尾:依實際大小建立最終檔案 -----
UnmapViewOfFile(pView);
CloseHandle(hMap);

// 建立最終檔案,大小 = 實際接收位元組數
HANDLE hFinal = CreateFile(L"Recv.dat",
    GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
    CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
HANDLE hFinalMap = CreateFileMapping(hFinal, NULL, PAGE_READWRITE,
    (DWORD)(fileOffset >> 32), (DWORD)(fileOffset & 0xFFFFFFFF), NULL);
CloseHandle(hFinal);

BYTE* pFinalView = (BYTE*)MapViewOfFile(hFinalMap, FILE_MAP_ALL_ACCESS,
    0, 0, (DWORD)fileOffset);
memcpy(pFinalView, pView, (size_t)fileOffset);

UnmapViewOfFile(pFinalView);
CloseHandle(hFinalMap);
CloseHandle(hMap);
DeleteFile(L"Recv.tmp");

進階技巧

  • FlushViewOfFile:強制將髒頁寫回磁碟,確保關鍵資料持久化,減少掉電遺失風險。
  • 大檔案隨機存取:以 64 KB 為單位動態映射所需區塊,UnmapViewOfFile 釋放不再需要的視圖,維持虛擬位址空間穩定(32 位元程序特別重要)。
  • 跨進程共享記憶體:CreateFileMapping 指定名稱(lpName),另一進程用 OpenFileMapping 即可共享同一塊實體記憶體,實現高效 IPC。
  • 32 位元程序處理 >4 GB 檔案:分段映射(滑動視窗)是唯一可行策略;64 位元程序可一次映射整個檔案。

效能對比

實測顯示,對 50 GB 循序寫入:

  • WriteFile + 8 MB 緩衝區:約 180 秒
  • 記憶體對應檔(分段映射):約 95 秒(提升 ~2 倍)

優勢來源:省去使用者模式緩衝區拷貝、系統自動最佳化分頁寫入順序、CPU 可直接透過指標運算處理資料。

結語

記憶體對應檔案是 Windows 核心提供的強大機制,適用於超大檔案處理、高效能資料記錄、進程間共享記憶體等場景。掌握 CreateFileMapping/MapViewOfFile 的分段映射技巧,即可在 32/64 位元環境下游刃有餘地處理 TB 級資料。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved