0

Windows アプリケーション内にロードされる DLL のメモリ範囲内で一定量のメモリを割り当てようとしています。

私が行っている方法はVirtualQuery()、空きとしてマークされ、割り当てを行う必要がある境界内にあるメモリ領域を検索するために使用しています。私が見ているのは、領域がマークされているにもかかわらずMEM_FREE VirtualAlloc()、メモリの割り当てに失敗することがあるということです。

コードは次のコードに非常に近いです。

LPVOID address = NULL, mem = NULL;

for (address = LOWER_RANGE; address < UPPER_RANGE;) {
    MEMORY_BASIC_INFORMATION mbi = {0};

    if (VirtualQuery(address, &mbi, sizeof(mbi))) {
        if (mbi.State == MEM_FREE && mbi.RegionSize >= ALLOC_SIZE) {
            mem = VirtualAlloc(address, ALLOC_SIZE, 
                MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
            if (mem) {
                break;
            }
        }
    }

    address = mbi.BaseAddress + mbi.RegionSize;
}

VirtualAlloc()失敗したらGetLastError()リターンERROR_INVALID_ADDRESS (487).

私がこれを回避した方法は、それが十分に大きい場合は、mbi.RegionSizeページ サイズのステップを使用してスキャンし、必要なメモリを割り当てることができるアドレスを見つけることです。

VirtualQuery領域全体が解放され、必要なアドレス内に割り当てることができるはずですが、通常、最初のアドレスがVirtualAlloc失敗すると、最終的に成功するまでいくつかのステップをループする必要があるのはなぜですか。

4

2 に答える 2

5

VirtualAlloc にアドレスを提供し、MEM_RESERVE フラグを使用すると、アドレスは割り当て粒度 (64K) の最も近い倍数に切り捨てられます。おそらく、空きページの領域が、完全に予約されていない割り当てられた 64K ブロックにあることがわかります。これらのブロック内の予約されていないページは、割り当てられたブロック内のすべてのページが解放されるまで割り当て (または予約) できません。

VirtualAllocの MSDN ドキュメントから:

lpAddress [入力、オプション]

割り当てる領域の開始アドレス。メモリが予約されている場合、指定されたアドレスは割り当て粒度の最も近い倍数に切り捨てられます。[...] ページのサイズとホスト コンピューターでの割り当ての粒度を確認するには、 GetSystemInfo関数を使用します。

于 2015-07-30T18:17:05.603 に答える
0

自分に合った解決策を見つけました。前の例では、割り当てと予約を同時に行おうとしていました。私が使用していたアドレスは、割り当ての粒度と一致していませんでした。そのため、リージョン内に収まる割り当ての粒度の最も近い倍数に切り上げる必要がありました。

このようなものが機能しました(注、このコードはテストしていません)。

PVOID address = NULL, mem = NULL;

for (address = LOWER_RANGE; address < UPPER_RANGE;) {
    MEMORY_BASIC_INFORMATION mbi = {0};
    if (VirtualQuery(address, &mbi, sizeof(mbi))) {
        PVOID reserveAddr = mbi.BaseAddress + (mbi.BaseAddress % alloc_gran);
        PVOID end_addr = mbi.BaseAddress + mbi.RegionSize;

        if (mbi.State == MEM_FREE && 
                mbi.RegionSize >= ALLOC_SIZE &&
                reserveAddr + ALLOC_SIZE <= end_addr) {
            mem = VirtualAlloc(reserveAddr, ALLOC_SIZE, 
                MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READ);
            if (mem) {
                break;
            }
        }
    }

    address = mbi.BaseAddress + mbi.RegionSize;
}
于 2015-07-31T17:00:08.077 に答える