5

私は非常に大きなファイルを持っているので、それを小さな断片に読んでから、各断片を処理する必要があります。MapViewOfFile関数を使用してメモリ内のピースをマップしていますが、最初の部分を読み取った後、2番目の部分を読み取ることができません。マップしようとするとスローされます。

    char *tmp_buffer = new char[bufferSize];
    LPCWSTR input = L"input";   
    OFSTRUCT tOfStr;
    tOfStr.cBytes = sizeof tOfStr;

    HANDLE inputFile = (HANDLE)OpenFile(inputFileName, &tOfStr, OF_READ); 
    HANDLE fileMap = CreateFileMapping(inputFile, NULL, PAGE_READONLY, 0, 0, input);

    while (offset < fileSize)
    {
        long k = 0;
        bool cutted = false;
        offset -= tempBufferSize;

        if (fileSize - offset <= bufferSize)
        {
            bufferSize = fileSize - offset;
        }

        char *buffer = new char[bufferSize + tempBufferSize];

        for(int i = 0; i < tempBufferSize; i++)
        {
            buffer[i] = tempBuffer[i];
        }

        char *tmp_buffer = new char[bufferSize];
        LPCWSTR input = L"input";
        HANDLE inputFile;
        OFSTRUCT tOfStr;
        tOfStr.cBytes = sizeof tOfStr;

        long long offsetHigh = ((offset >> 32) & 0xFFFFFFFF);
        long long offsetLow = (offset & 0xFFFFFFFF);

        tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);

        memcpy(&buffer[tempBufferSize], &tmp_buffer[0], bufferSize);

        UnmapViewOfFile(tmp_buffer);

        offset += bufferSize;
        offsetHigh = ((offset >> 32) & 0xFFFFFFFF);
        offsetLow = (offset & 0xFFFFFFFF);

        if (offset < fileSize)
        {
            char *next;
            next = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, 1);

            if (next[0] >= '0' && next[0] <= '9')
            {
                cutted = true;
            }

            UnmapViewOfFile(next);
        }

        ostringstream path_stream;
        path_stream << tempPath << splitNum;

        ProcessChunk(buffer, path_stream.str(), cutted, bufferSize);

        delete buffer;

        cout << (splitNum + 1) << " file(s) sorted" << endl;
        splitNum++;
    }
4

3 に答える 3

7

1 つの可能性は、割り当ての粒度の倍数であるオフセットを使用していないことです。MSDN から:

高オフセットと低オフセットの組み合わせは、ファイル マッピング内のオフセットを指定する必要があります。また、システムのメモリ割り当ての粒度と一致する必要があります。つまり、オフセットは割り当ての粒度の倍数である必要があります。システムのメモリ割り当ての粒度を取得するには、SYSTEM_INFO 構造体のメンバーを埋める GetSystemInfo 関数を使用します。

割り当ての粒度の倍数以外でマッピングしようとすると、マッピングは失敗し、GetLastErrorが返されERROR_MAPPED_ALIGNMENTます。

それ以外にも、コード サンプルには多くの問題があり、何をしようとしているのか、どこが間違っているのかを確認するのが非常に難しくなっています。少なくとも、メモリ リークを解決する必要があります。完全に不要なバッファを割り当ててリークしているようです。より適切な名前を付けることで、実際の用途が明確になります。

次に、MapViewOfFile の呼び出しにブレークポイントを設定し、渡すすべてのパラメーター値をチェックして、それらが正しく表示されることを確認することをお勧めします。まず、2 回目の呼び出しでは、offsetHigh が 0 で、offsetLow が bufferSize であると予想します。

バットからいくつかの疑わしいもの:

HANDLE inputFile = (HANDLE)OpenFile(inputFileName, &tOfStr, OF_READ); 

すべてのキャストはあなたを疑わしくさせるはずです。必要な場合もありますが、その理由を理解していることを確認してください。この時点で、使用している他のすべてのファイル API が を必要とするのにHANDLE、この関数が を返す理由を自問する必要がありますHFILEOpenFile のドキュメントを確認すると、「この関数は機能が制限されているため、お勧めしません。新しいアプリケーションの開発には、CreateFile 関数を使用してください。」と表示されます。既存のファイルを開きたいので混乱するように聞こえるかもしれませんが、CreateFile はまさにそれを行うことができ、正しい型を返します。

long long offsetHigh = ((offset >> 32) & 0xFFFFFFFF);

は何型offsetですか?unsigned long longおそらく、それがまたは同等であることを確認したいでしょう。特に右にビットシフトするときは、ほとんどの場合、符号拡張を避けるために符号なしの型が必要です。また、シフトする量よりも多くのビットを持つ型であることを確認する必要があります.32ビットの値を32(またはそれ以上)ビットシフトすることは、実際にはCおよびC ++では未定義です。特定のタイプの最適化を行います。

long long offsetLow = (offset & 0xFFFFFFFF);

これらのステートメントの両方で、値に注意する必要があり0xFFFFFFFFます。キャストも接尾辞も付けていないため、コンパイラがそれを int または unsigned int のどちらとして扱うかを予測するのは難しい場合があります。この場合、unsigned int になりますが、それは多くの人にとって明らかではありません。実際、この回答を最初に書いたとき、私はこれを間違えました。[この段落は 2017 年 5 月 16 日に修正されました] ビット演算では、ほとんどの場合、符号なしの値を使用していることを確認する必要があります。

tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);

and を符号付きの値である s にキャストoffsetHighoffsetLowています。intAPI は実際にDWORDは、符号なしの値である s を必要としています。呼び出しでキャストするのではなく、offsetHighandoffsetLowDWORDs として宣言し、次のように初期化でキャストします。

DWORD offsetHigh = static_cast<DWORD>((offset >> 32) & 0xFFFFFFFFul);
DWORD offsetLow  = static_cast<DWORD>( offset        & 0xFFFFFFFFul);
tmp_buffer = reinterpret_cast<const char *>(MapViewOfFile(fileMap, FILE_MAP_READ, offsetHigh, offsetLow, bufferSize));

これらの修正により、問題が解決される場合とされない場合があります。不完全なコード サンプルから何が起こっているのかを伝えるのは困難です。

比較できる実際のサンプルを次に示します。

// Calls ProcessChunk with each chunk of the file.
void ReadInChunks(const WCHAR *pszFileName) {
  // Offsets must be a multiple of the system's allocation granularity.  We
  // guarantee this by making our view size equal to the allocation granularity.
  SYSTEM_INFO sysinfo = {0};
  ::GetSystemInfo(&sysinfo);
  DWORD cbView = sysinfo.dwAllocationGranularity;

  HANDLE hfile = ::CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ,
                               NULL, OPEN_EXISTING, 0, NULL);
  if (hfile != INVALID_HANDLE_VALUE) {
    LARGE_INTEGER file_size = {0};
    ::GetFileSizeEx(hfile, &file_size);
    const unsigned long long cbFile =
        static_cast<unsigned long long>(file_size.QuadPart);

    HANDLE hmap = ::CreateFileMappingW(hfile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (hmap != NULL) {
      for (unsigned long long offset = 0; offset < cbFile; offset += cbView) {
        DWORD high = static_cast<DWORD>((offset >> 32) & 0xFFFFFFFFul);
        DWORD low  = static_cast<DWORD>( offset        & 0xFFFFFFFFul);
        // The last view may be shorter.
        if (offset + cbView > cbFile) {
          cbView = static_cast<int>(cbFile - offset);
        }
        const char *pView = static_cast<const char *>(
            ::MapViewOfFile(hmap, FILE_MAP_READ, high, low, cbView));
        if (pView != NULL) {
          ProcessChunk(pView, cbView);
        }
      }
      ::CloseHandle(hmap);
    }
    ::CloseHandle(hfile);
  }
}
于 2012-03-27T17:04:49.267 に答える
1

また、メモリ マップ ファイルにも問題がありました。基本的に、同じ PC 上の 2 つのアプリ間でメモリ (1Mo) を共有したかっただけです。- Delphiで書かれた両方のアプリ - Windows8 Proを使用

最初は、1 つのアプリケーション (最初に起動したアプリケーション) は の読み取りと書き込みができましmemoryMappedFileたが、2 番目のアプリケーションは読み取りしかできませんでした ( error 5 : AccessDenied)

最後に、多くのテストを行った後、両方のアプリケーションでCreateFileMapping. セキュリティ記述子を作成しようとしましたが、何も役に立ちませんでした。

アプリケーションが最初に呼び出される直前に、最初の呼び出しが失敗したOpenFileMapping場合CreateFileMapping

MemoryMappedFile私を誤解させたもう1つのことは、両方のアプリケーションで異なる場所で同じものを目に見えて参照しているにもかかわらず、ハンドルです。

最後に、この修正の後、アプリケーションは問題なく動作するように見えましたが、しばらくすると error_NotEnough_Memory が発生しました。MapViewOfFile を呼び出すとき。これは私の初歩的なミスであり、常に UnmapViewOfFile を呼び出していたわけではありません。

于 2012-09-24T01:38:11.447 に答える
1

コードにメモリ リークがあります。

char *tmp_buffer = new char[bufferSize];
[ ... ]
while (offset < fileSize)
{
[ ... ]
    char *tmp_buffer = new char[bufferSize];
[ ... ]
    tmp_buffer = (char *)MapViewOfFile(fileMap, FILE_MAP_READ, (int)offsetHigh, (int)offsetLow, bufferSize);
[ ... ]
}

そこのすべての反復中にdelete割り当てたものではありません。new char[]ファイルが十分に大きい場合、またはこのループを十分に繰り返すと、メモリ割り当ては最終的に失敗します。その場合throw()、アロケータによって完了が表示されます。

のような Win32 API 呼び出しMapViewOfFile()は C++ ではなく、決してスローされず、エラー コードを返します (後者NULLは失敗した場合)。したがって、例外が表示される場合は、C++ コードに問題があります。上記の可能性が高いです。

于 2012-03-27T13:06:55.157 に答える