C++メモリマッピングによる大ファイルの読み書き

4726 ワード

会社のプロジェクトの要求のため、コンポーネントAはミリ秒ごとに5百万本のデータを発生して、ストレージを行う必要があって、IOストリームの速度を使うのは遅すぎて、そこでメモリのマッピングの方法を採用してストレージを行うことを決定して、効率は多くて、そこでクエリーの資料、およびコードを書く過程の中で出会ったいくつかの問題に対して整理します.
メモリマッピングには、いくつかの重要なWindows APIが必要です.
HANDLE CreateFile(LPCTSTR lpFileName,
                    DWORD dwDesiredAccess,
                    DWORD dwShareMode,
                    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
                    DWORD dwCreationDisposition,
                    DWORD dwFlagsAndAttributes,
                    HANDLE hTemplateFile);

関数CreateFile()は、ファイルカーネルオブジェクトを作成/開くために使用され、ハンドルを返します.この関数を呼び出すときに、データの読み書きとファイルの共有方法に基づいてパラメータdwDesiredAccessとdwShareModeを設定する必要があります.誤ったパラメータ設定は、対応する操作に失敗します.
具体的な使い方は、最後のコードを参照してください.
2つ目:
HANDLE CreateFileMapping(HANDLE hFile,
                           LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
                           DWORD flProtect,
                           DWORD dwMaximumSizeHigh,
                           DWORD dwMaximumSizeLow,
                           LPCTSTR lpName);

プロセスアドレス空間にマッピングするファイルハンドル(CreateFile()関数の戻り値によって取得される)をパラメータhFileで指定するファイルマッピングカーネルオブジェクトを作成します.メモリマッピングファイルの物理メモリは、実際には、システムのページファイルから割り当てられたメモリではなく、ディスクに格納されたファイルであるため、システムはアドレス空間領域をアクティブに保持することも、ファイルの記憶領域を自動的にマッピングすることもなく、ページに対してどのような保護属性をとるかをシステムが決定できるようにするために、パラメータflProtectで設定する必要があり、属性PAGE_を保護するREADONLY、PAGE_READWRITEとPAGE_WRITECOPYは、それぞれファイルマッピングオブジェクトがマッピングされた後、ファイルデータの読み取り、書き込みが可能であることを示す.PAGEを使用していますREADONLYの場合、CreateFile()がGENERIC_を採用していることを確認する必要がありますREADパラメータ;PAGE_READWRITEではCreateFile()にGENERIC_を採用するよう求めているREAD|GENERIC_WRITEパラメータ;属性PAGE_についてWRITECOPYはCreateFile()がGENERICを採用していることを確認するだけです.READとGENERIC_WRITEのうちの1つでよい.DWORD型のパラメータdwMaximumSizeHighとdwMaximumSizeLowもかなり重要であり、ファイルの最大バイト数を指定している.この2つのパラメータは64ビットであるため、サポートされている最大ファイル長は16 EBであり、どのようなビッグデータ量のファイル処理の場合の要求をほぼ満たすことができる.
3つ目:
LPVOID MapViewOfFile(HANDLE hFileMappingObject,
                        DWORD dwDesiredAccess,
                        DWORD dwFileOffsetHigh,
                        DWORD dwFileOffsetLow,
                        DWORD dwNumberOfBytesToMap);

MapViewOfFile()関数は、ファイルデータをプロセスのアドレス空間にマッピングし、パラメータhFileMappingObjectはCreateFileMapping()が返すファイルイメージオブジェクトハンドルです.パラメータdwDesiredAccessは、ファイルデータへのアクセス方法を再指定し、CreateFileMapping()関数で設定した保護属性と一致します.ここでは、保護プロパティを繰り返し設定する必要はありませんが、アプリケーションがデータの保護プロパティをより効果的に制御できるようにします.MapViewOfFile()関数では、すべてまたは一部のマッピングファイルを許可します.マッピング時には、データファイルのオフセットアドレスとマッピングする長さを指定する必要があります.ここで、ファイルのオフセットアドレスは、DWORD型のパラメータdwFileOffsetHighとdwFileOffsetLowからなる64ビット値で指定され、オペレーティングシステムの割当粒度の整数倍である必要があり、Windowsオペレーティングシステムでは、割当粒度は64 KBに固定されている.もちろん、現在のオペレーティングシステムの割り当ての粒度は、次のコードで動的に取得することもできます.
SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;

プロセスアドレス空間領域にマッピングされたファイルの処理が完了したら、関数UnmapViewOfFile()でファイルデータイメージの解放とファイルのクローズを完了する必要があります.
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);

一意のパラメータlpBaseAddressは、戻り領域のベースアドレスを指定し、MapViewOfFile()の戻り値に設定する必要があります.関数MapViewOfFile()を使用した後は、対応するUnmapViewOfFile()呼び出しが必要です.そうしないと、プロセスが終了する前に、保持されている領域は解放されません.これに加えて、前にCreateFile()とCreateFileMapping()関数によってファイルカーネルオブジェクトとファイルマッピングカーネルオブジェクトが作成されましたが、プロセスが終了する前にCloseHandle()を介して解放する必要があります.そうしないと、リソース漏洩の問題が発生します.
具体的には、次のようになります.
#include 
#include  
#include 
#include 
using namespace std;
 
int main()
{
    const char* shared_name = "test";
    const char* file_name = "C:\\1.txt";
    const DWORD mmf_size = 512*1024;
    //    
    DWORD access_mode = (GENERIC_READ|GENERIC_WRITE);
    //    
    DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    //    
    DWORD flags = FILE_FLAG_SEQUENTIAL_SCAN;//|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING;
    DWORD error_code;
     
    //    
	//const char *   LPCWSTR
    char* szStr = "Mikasoi";  
	CString str = CString(file_name);
	USES_CONVERSION;
	LPCWSTR wszClassName = A2CW(W2A(str));
	str.ReleaseBuffer();
	
    //    
    HANDLE mmHandle =
        CreateFile(wszClassName,
             access_mode, 
             share_mode, 
             NULL, 
             OPEN_ALWAYS,
             flags,
             NULL);
 
    if (mmHandle == INVALID_HANDLE_VALUE) 
	{
        error_code = GetLastError();
        cout<

このコードは問題ないはずですが、会社で書いたもので、家で書いたブログは、記憶が少しずれているかもしれませんが、コンパイルミスも起こらないはずです.
 
添付:参考資料:
https://blog.csdn.net/mikasoi/article/details/81347854
https://www.cnblogs.com/yukaizhao/archive/2011/05/18/MapViewOfFile_CreateFileMapping.html