メモリファイルマッピング(MMAP)

5811 ワード

原文アドレス:メモリファイルマッピング(MMAP)作成者:Arthursky
ファイル操作はアプリケーションの最も基本的な機能の一つであり、Win 32 APIとMFCはいずれもファイル処理をサポートする関数とクラスを提供し、よく使われるのはWin 32 APIのCreateFile()、WriteFile()、ReadFile()とMFCが提供するCFIleクラスなどである.一般的に、これらの関数は多くの場合の要求を満たすことができるが、いくつかの特殊な応用分野に必要な数十GB、数百GB、さらには数TBの大量の記憶に対して、通常のファイル処理方法で処理することは明らかに通用しない.現在、上記のような大きなファイルの動作は、メモリマッピングファイルとして一般的に処理されているが、本稿では、このようなWindowsコアプログラミング技術について議論する.
従来のファイルアクセス方式では、まずopenシステム呼び出しでファイルを開く、read、write、lseekなどの呼び出しを用いて順番または直後のI/Oを行う方式は非常に非効率であり、I/O操作のたびにシステム呼び出しが必要である.また、複数のプロセスが同じファイルにアクセスする場合、各プロセスは自分のアドレス空間でコピーを維持し、メモリ空間を浪費する.一方、プロセスのアドレス空間にページを一定のメカニズムでマッピングできる場合、すなわち、まず、いくつかのメモリ管理データ構造を簡単に生成することによってマッピングの作成を完了する.プロセスがページにアクセスするときに欠落ページ割り込みが発生し、カーネルはページをメモリに読み込み、ページテーブルを更新してページを指す.また、この方式は、同一のコピーの共有に非常に便利である.
メモリマッピングファイルを使用すると、WIN 32プロセスの仮想アドレス空間にメモリ領域を保持し、ターゲットファイルをこの仮想メモリにマッピングできます.メモリデータにアクセスすることで、ファイル内のデータをメモリに格納するように直接操作できます.実際には、API関数を呼び出してファイルを読み書きする必要もなく、バッファアルゴリズムを自分で提供する必要もなく、オペレーティングシステムがこれらの作業を完了します.メモリマッピングファイルを使用すると、プログラム開発が大幅に便利になり、プログラムの実行効率も非常に高くなります.
ディスクファイルの一部または全部をメモリに直接マッピングすると、ファイル内の情報位置がメモリに対応するアドレス空間を持つようになり、read/write関数を必要とせずにファイルの読み書きをポインタで直接行うことができます.同時に、オペレーティングシステムはデータのリフレッシュをディスクに保存します.
各WIN 32プロセスは自分のアドレス空間を持っていて、1つのWIN 32プロセスは別のプロセスのアドレス空間の私有データにアクセスすることができなくて、2つのプロセスは同じ値を持つポインタでアドレスすることができて、しかし読み書きしたのはそれらのそれぞれのデータだけで、このようにプロセスの間の相互干渉を大幅に減らして、システムの丈夫性を強化しました;一方、各WIN 32プロセスは4 GBのアドレス空間を持っているが、実際に4 GBの実際の物理メモリを持っているわけではない.オペレーティングシステムがCPUのメモリページング機能を利用して提供する仮想アドレス空間だけである.一般的に、ほとんどの仮想アドレスには物理メモリがありません.これらのアドレス空間を本当に使用できる前に、オペレーティングシステムによって実際の物理メモリも提供されます.仮想アドレスに実際の物理メモリを提供するプロセスを「コミット」(Commit)と呼びます.場合によっては、システムがコミットする物理メモリのタイプが異なり、RAMまたはハードディスクシミュレーションの仮想メモリである可能性があります.
まずCreateFile()関数を使用して、ディスクがメモリマッピングファイルとして使用されるファイルを識別するファイルカーネルオブジェクトを作成または開きます.CreateFile()でファイルイメージを物理メモリの場所でオペレーティングシステムに通知すると、イメージファイルのパスのみが指定され、イメージの長さは指定されていません.ファイル・マッピング・オブジェクトにどれだけの物理記憶領域が必要かを指定するには、CreateFileMapping()関数を使用して、システム・ファイルのサイズとファイルへのアクセス方法を示すファイル・マッピングカーネル・オブジェクトを作成する必要があります.ファイルマッピングオブジェクトを作成した後、ファイルデータのアドレス空間領域を保持し、ファイルデータをその領域にマッピングされた物理メモリとしてコミットする必要があります.MapViewOfFile()関数は、システムの管理によってファイルマッピングオブジェクトのすべてまたは一部をプロセスアドレス空間にマッピングする役割を果たします.この場合、メモリマッピングファイルの使用と処理は、通常メモリにロードされるファイルデータの処理と基本的に同様であり、メモリマッピングファイルの使用が完了した場合には、一連の操作によってリソースの消去と使用済みリソースの解放も完了する.この部分は比較的簡単で、プロセスのアドレス空間からファイルデータを元に戻すイメージ、CloseHandle()で前に作成したファイルマッピングオブジェクトとファイルオブジェクトを閉じることができます.
メモリマッピングファイル関連関数
メモリマッピングファイルを使用する場合、使用するAPI関数は、主に前述したいくつかの関数です.以下では、それぞれ説明します.
HANDLE CreateFile(LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, 
HANDLE hTemplateFile);

関数CreateFile()は、通常のファイル操作でもファイルを作成、開くためによく使用されます.メモリマッピングファイルを処理するときに、ファイルカーネルオブジェクトを作成/開くために使用され、ハンドルを返します.この関数を呼び出すときに、データの読み書きとファイルの共有方法に基づいてパラメータdwDesign AccessとdwShareModeを設定する必要があります.パラメータの設定が間違っていると、対応する操作に失敗します.
HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);

CreateFileMapping()関数は、プロセスアドレス空間にマッピングされるファイルハンドル(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であり、どのようなビッグデータ量のファイル処理の場合の要求をほぼ満たすことができる.
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;

パラメータdwNumberOfBytesToMapは、データファイルのマッピング長を指定します.ここで特に、Windows 9 xオペレーティングシステムでは、ファイルマッピングオブジェクト全体を格納するのに十分な領域が見つからない場合、空の値(NULL)が返されます.ただし、Windows 2000では、必要なビューのために十分な領域を見つけるだけで、ファイル全体のマッピングオブジェクトのサイズを考慮する必要はありません.
プロセスアドレス空間領域にマッピングされたファイルの処理が完了すると、関数UnmapViewOfFile()によってファイルデータイメージの解放が完了する必要があります.この関数プロトタイプは次のように宣言されます.
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);

一意のパラメータlpBaseAddressは、戻り領域のベースアドレスを指定し、MapViewOfFile()の戻り値に設定する必要があります.関数MapViewOfFile()を使用した後は、対応するUnmapViewOfFile()呼び出しが必要です.そうしないと、プロセスが終了する前に、保持されている領域は解放されません.これに加えて、前にCreateFile()とCreateFileMapping()関数によってファイルカーネルオブジェクトとファイルマッピングカーネルオブジェクトが作成されましたが、プロセスが終了する前にCloseHandle()を介して解放する必要があります.そうしないと、リソース漏洩の問題が発生します.
前述の必須API関数に加えて、メモリマッピングファイルを使用する場合には、状況に応じて他の補助関数を選択します.たとえば、メモリマッピングファイルを使用する場合、速度を向上させるためにファイルのデータページをキャッシュし、ファイルマッピングビューを処理するときにファイルのディスクイメージをすぐに更新しません.この問題を解決するために、FlushViewOfFile()関数を使用することを考慮します.この関数は、変更したデータの一部またはすべてをディスクイメージに再書き込みすることを強制し、すべてのデータ更新がディスクにタイムリーに保存されることを保証します.