VirtualAlloc2 関数 (memoryapi.h)

指定したプロセスの仮想アドレス空間内のメモリ領域の状態を予約、コミット、または変更します (割り当てられたメモリは 0 に初期化されます)。

構文

PVOID VirtualAlloc2(
  [in, optional]      HANDLE                 Process,
  [in, optional]      PVOID                  BaseAddress,
  [in]                SIZE_T                 Size,
  [in]                ULONG                  AllocationType,
  [in]                ULONG                  PageProtection,
  [in, out, optional] MEM_EXTENDED_PARAMETER *ExtendedParameters,
  [in]                ULONG                  ParameterCount
);

パラメーター

[in, optional] Process

プロセスへのハンドル。 関数は、このプロセスの仮想アドレス空間内にメモリを割り当てます。

ハンドルには 、PROCESS_VM_OPERATION アクセス権が必要です。 詳細については、「 プロセス のセキュリティとアクセス権」を参照してください。

ProcessNULL の場合、関数は呼び出し元のプロセスにメモリを割り当てます。

[in, optional] BaseAddress

割り当てるページの領域に必要な開始アドレスを指定するポインター。

BaseAddressNULL の場合、関数はリージョンを割り当てる場所を決定します。

BaseAddressNULL でない場合、指定されたMEM_ADDRESS_REQUIREMENTS構造体はすべてゼロで構成され、ベース アドレスはシステム割り当て粒度の倍数である必要があります。 割り当ての細分性を確認するには、 GetSystemInfo 関数を使用します。

このアドレスが InitializeEnclave を呼び出して初期化していないエンクレーブ内にある場合、 VirtualAlloc2 はそのアドレスのエンクレーブに 0 のページを割り当てます。 ページは以前にコミットされていない必要があり、Intel Software Guard Extensions プログラミング モデルの EEXTEND 命令では測定されません。

初期化したエンクレーブ内の アドレスが の場合、割り当て操作は ERROR_INVALID_ADDRESS エラーで失敗します。 これは、動的メモリ管理 (つまり SGX1) をサポートしないエンクレーブに当てはまります。 SGX2 エンクレーブは割り当てを許可し、ページが割り当てられた後にエンクレーブによって受け入れられる必要があります。

[in] Size

割り当てるメモリ領域のサイズ (バイト単位)。

サイズは常にページ サイズの倍数である必要があります。

BaseAddressNULL でない場合、関数は BaseAddress から BaseAddress+Size までの範囲内の 1 つ以上のバイトを含むすべてのページを割り当てます。 つまり、たとえば、ページ境界をまたぐ 2 バイト範囲の場合、関数は両方のページを割り当てます。

[in] AllocationType

メモリ割り当ての種類。 このパラメーターには、次のいずれかの値が含まれている必要があります。

意味
MEM_COMMIT
0x00001000
指定された予約済みメモリ ページに対して (メモリの全体的なサイズとディスク上のページング ファイルから) メモリ料金を割り当てます。 また、関数は、呼び出し元が後でメモリに最初にアクセスするときに、内容が 0 になることも保証します。 仮想アドレスが実際にアクセスされるまで、実際の物理ページは割り当てられません。

1 つのステップでページを予約してコミットするには、 で VirtualAlloc2MEM_COMMIT | MEM_RESERVEを呼び出します。

MEM_RESERVEを指定せずに MEM_COMMITを指定して特定のアドレス範囲をコミットしようとすると、範囲全体が既に予約されていない限り、NULL 以外の BaseAddress が失敗します。 結果のエラー コードは ERROR_INVALID_ADDRESS

既にコミットされているページをコミットしようとしても、関数は失敗しません。 つまり、各ページの現在のコミットメント状態を最初に決定することなく、ページをコミットできます。

BaseAddress がエンクレーブ内のアドレスを指定する場合は、AllocationTypeMEM_COMMITする必要があります。

MEM_RESERVE
0x00002000
メモリまたはディスク上のページング ファイルに実際の物理ストレージを割り当てずに、プロセスの仮想アドレス空間の範囲を予約します。

予約ページをコミットするには、MEM_COMMITを使用して VirtualAlloc2 をもう一度呼び出します。 1 つのステップでページを予約してコミットするには、 で VirtualAlloc2MEM_COMMIT | MEM_RESERVEを呼び出します。

mallocLocalAlloc などの他のメモリ割り当て関数では、解放されるまで予約済みメモリを使用できません。

MEM_REPLACE_PLACEHOLDER
0x00004000
プレースホルダーを通常のプライベート割り当てに置き換えます。 data/pf-backed セクション ビューのみがサポートされています (イメージ、物理メモリなどはサポートされていません)。 プレースホルダーを置き換える場合、 BaseAddressSize はプレースホルダーのものと完全に一致する必要があり、指定された MEM_ADDRESS_REQUIREMENTS 構造体はすべてゼロで構成されている必要があります。

プレースホルダーをプライベート割り当てに置き換えた後、その割り当てをプレースホルダーに解放するには、VirtualFree と VirtualFreeExdwFreeType パラメーターを参照してください。

プレースホルダーは、予約済みメモリ領域の一種です。

MEM_RESERVE_PLACEHOLDER
0x00040000
プレースホルダーを作成するには、 を使用して VirtualAlloc2MEM_RESERVE | MEM_RESERVE_PLACEHOLDER 呼び出し、 PageProtectionPAGE_NOACCESS に設定します。 プレースホルダーを解放/分割/結合するには、VirtualFree と VirtualFreeExdwFreeType パラメーターを参照してください。

プレースホルダーは、予約済みメモリ領域の一種です。

MEM_RESET
0x00080000
BaseAddressSize で指定されたメモリ範囲内のデータが関心を持たなくなったことを示します。 ページをページング ファイルから読み取ったり、ページング ファイルに書き込んだりしないでください。 ただし、メモリ ブロックは後で再度使用されるため、デコミットしないでください。 この値は、他の値と共に使用することはできません。

この値を使用しても、 MEM_RESET で操作される範囲にゼロが含まれる保証はありません。 範囲にゼロを含める場合は、メモリをデコミットしてから、再コミットします。

MEM_RESETを使用すると、VirtualAlloc2 関数は fProtect の値を無視します。 ただし、 fProtect を有効な保護値 ( PAGE_NOACCESSなど) に設定する必要があります。

MEM_RESETを使用し、メモリの範囲がファイルにマップされている場合、VirtualAlloc2 はエラーを返します。 共有ビューは、ページング ファイルにマップされている場合にのみ使用できます。

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO は、MEM_RESETが正常に 適用されたアドレス 範囲でのみ呼び出す必要があります。 これは、BaseAddress および Size で指定された指定されたメモリ範囲内のデータが呼び出し元にとって重要であることを示し、MEM_RESETの影響を元に戻そうとします。 関数が成功した場合は、指定されたアドレス範囲のすべてのデータはそのままであることを意味します。 関数が失敗した場合、アドレス範囲のデータの少なくとも一部がゼロに置き換えられました。

この値は、他の値と共に使用することはできません。 以前 にMEM_RESET されなかったアドレス範囲で MEM_RESET_UNDO が呼び出された場合、動作は未定義です。 MEM_RESETを指定すると、VirtualAlloc2 関数は PageProtection の値を無視します。 ただし、 PageProtection を有効な保護値 ( PAGE_NOACCESSなど) に設定する必要があります。

Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003、Windows XP: MEM_RESET_UNDO フラグは、Windows 8 および Windows Server 2012 までサポートされていません。

 

このパラメーターは、示されているとおりに次の値を指定することもできます。

意味
MEM_LARGE_PAGES
0x20000000
大きなページのサポートを使用してメモリを割り当てます。

サイズと配置は、大きなページの最小値の倍数である必要があります。 この値を取得するには、 GetLargePageMinimum 関数を使用します。

この値を指定する場合は、 MEM_RESERVEMEM_COMMITも指定する必要があります。

MEM_PHYSICAL
0x00400000
アドレスウィンドウ拡張 (AWE) ページのマップに使用できるアドレス範囲を予約します。

この値は 、MEM_RESERVE で使用する必要があり、その他の値は使用できません。

MEM_TOP_DOWN
0x00100000
可能な限り高いアドレスでメモリを割り当てます。 これは、特に割り当てが多い場合に、通常の割り当てよりも遅くなる可能性があります。

[in] PageProtection

割り当てられるページの領域のメモリ保護。 ページがコミットされている場合は、 メモリ保護定数のいずれかを指定できます。

BaseAddress でエンクレーブ内のアドレスが指定されている場合、PageProtection には次の値を指定できません。

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

エンクレーブに動的メモリを割り当てる場合は、 PageProtection パラメーターを PAGE_READWRITE または PAGE_EXECUTE_READWRITEする必要があります。

[in, out, optional] ExtendedParameters

MEM_EXTENDED_PARAMETER型の 1 つ以上の拡張パラメーターへの省略可能なポインター。 これらの拡張パラメーター値はそれぞれ、MemExtendedParameterAddressRequirements または MemExtendedParameterNumaNode のいずれかの Type フィールドを持つことができます。 MemExtendedParameterNumaNode 拡張パラメーターが指定されていない場合、動作は VirtualAlloc/MapViewOfFile 関数と同じです (つまり、物理ページに推奨される NUMA ノードは、最初にメモリにアクセスするスレッドの理想的なプロセッサに基づいて決定されます)。

[in] ParameterCount

ExtendedParameters が指す拡張パラメーターの数。

戻り値

関数が成功した場合、戻り値はページの割り当てられた領域のベース アドレスです。

関数が失敗した場合は、返される値は NULL です。 詳細なエラー情報を得るには、GetLastError を呼び出します。

解説

この関数を使用すると、次を指定できます。

  • 仮想アドレス空間の範囲と、新しい割り当てに対する power-of-2 アラインメント制限
  • 任意の数の拡張パラメーター
  • 拡張パラメーターとしての物理メモリの優先 NUMA ノード ( ExtendedParameters パラメーターを参照)
  • プレースホルダー操作 (具体的には置換)。

この API は、高性能なゲームやサーバー アプリケーションをサポートするために、仮想メモリを管理するための特殊な手法を提供します。 たとえば、プレースホルダーを使用すると、予約済みのメモリ範囲を明示的にパーティション分割、オーバーレイ、再マップできます。これは、任意に拡張可能な領域または仮想メモリ リング バッファーを実装するために使用できます。 VirtualAlloc2 では、特定のメモリアラインメントを使用してメモリを割り当てることもできます。

各ページには、関連付けられた ページの状態がありますVirtualAlloc2 関数は、次の操作を実行できます。

  • 予約ページのリージョンをコミットする
  • 無料ページのリージョンを予約する
  • 無料ページの領域を同時に予約してコミットする

VirtualAlloc2 は、既にコミットされているが、既に予約されているページを予約できないページをコミットできます。 つまり、既にコミットされているかどうかに関係なく、ページの範囲をコミットでき、関数は失敗しません。 ただし、既にコミットされている多数のページをコミットすると 、VirtualAlloc2 の呼び出しにかかる時間が大幅に長くなる可能性があるため、一般に、ほとんどコミットされていないページの最小範囲のみを指定する必要があります。

VirtualAlloc2 を使用してページブロックを予約し、VirtualAlloc2 を追加で呼び出して予約ブロックから個々のページをコミットできます。 こうすることで、プロセスで必要になるまで物理ストレージを使わずに、仮想アドレス空間の範囲を予約することができます。

lpAddress パラメーターが NULL でない場合、関数は lpAddress パラメーターと dwSize パラメーターを使用して、割り当てられるページの領域を計算します。 ページの範囲全体の現在の状態は、 flAllocationType パラメーターで指定された割り当ての種類と互換性がある必要があります。 それ以外の場合、関数は失敗し、どのページも割り当てされません。 この互換性要件は、既にコミットされているページのコミットを妨げるものではありません。上記の一覧を参照してください。

動的に生成されたコードを実行するには、 VirtualAlloc2 を使用してメモリを割り当て、 VirtualProtectEx 関数を使用して PAGE_EXECUTE アクセス権を付与します。

VirtualAlloc2 関数を使用すると、指定されたプロセスの仮想アドレス空間内のメモリのアドレス ウィンドウ拡張機能 (AWE) 領域を予約できます。 その後、このメモリ領域を使用して、アプリケーションで必要に応じて仮想メモリとの間で物理ページをマップできます。 MEM_PHYSICALMEM_RESERVEの値は、AllocationType パラメーターで設定する必要があります。 MEM_COMMIT値を設定することはできません。 ページ保護は 、PAGE_READWRITEに設定する必要があります。

VirtualFreeEx 関数は、コミットされたページのコミット解除、ページのストレージの解放、またはコミットされたページのコミット解除と解放を同時に行うことができます。 また、予約ページを解放して、無料のページにすることもできます。

実行可能なリージョンを作成する場合、呼び出し元のプログラムは、コードが設定されたら 、FlushInstructionCache への適切な呼び出しによってキャッシュの一貫性を確保する責任を負います。 そうしないと、新しく実行可能なリージョンからコードを実行しようとすると、予期しない結果が発生する可能性があります。

シナリオ 1. 同じ共有メモリ セクションの 2 つの隣接するビューをマッピングして、循環バッファーを作成します。

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

//
// This function creates a ring buffer by allocating a pagefile-backed section
// and mapping two views of that section next to each other. This way if the
// last record in the buffer wraps it can still be accessed in a linear fashion
// using its base VA.
//

void*
CreateRingBuffer (
    unsigned int bufferSize,
    _Outptr_ void** secondaryView
    )
{
    BOOL result;
    HANDLE section = nullptr;
    SYSTEM_INFO sysInfo;
    void* ringBuffer = nullptr;
    void* placeholder1 = nullptr;
    void* placeholder2 = nullptr;
    void* view1 = nullptr;
    void* view2 = nullptr;

    GetSystemInfo (&sysInfo);

    if ((bufferSize % sysInfo.dwAllocationGranularity) != 0) {
        return nullptr;
    }

    //
    // Reserve a placeholder region where the buffer will be mapped.
    //

    placeholder1 = (PCHAR) VirtualAlloc2 (
        nullptr,
        nullptr,
        2 * bufferSize,
        MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
        PAGE_NOACCESS,
        nullptr, 0
    );

    if (placeholder1 == nullptr) {
        printf ("VirtualAlloc2 failed, error %#x\n", GetLastError());
        goto Exit;
    }

    //
    // Split the placeholder region into two regions of equal size.
    //

    result = VirtualFree (
        placeholder1,
        bufferSize,
        MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER
    );

    if (result == FALSE) {
        printf ("VirtualFreeEx failed, error %#x\n", GetLastError());
        goto Exit;
    }

    placeholder2 = (void*) ((ULONG_PTR) placeholder1 + bufferSize);

    //
    // Create a pagefile-backed section for the buffer.
    //

    section = CreateFileMapping (
        INVALID_HANDLE_VALUE,
        nullptr,
        PAGE_READWRITE,
        0,
        bufferSize, nullptr
    );

    if (section == nullptr) {
        printf ("CreateFileMapping failed, error %#x\n", GetLastError());
        goto Exit;
    }

    //
    // Map the section into the first placeholder region.
    //

    view1 = MapViewOfFile3 (
        section,
        nullptr,
        placeholder1,
        0,
        bufferSize,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        nullptr, 0
    );

    if (view1 == nullptr) {
        printf ("MapViewOfFile3 failed, error %#x\n", GetLastError());
        goto Exit;
    }

    //
    // Ownership transferred, don’t free this now.
    //

    placeholder1 = nullptr;

    //
    // Map the section into the second placeholder region.
    //

    view2 = MapViewOfFile3 (
        section,
        nullptr,
        placeholder2,
        0,
        bufferSize,
        MEM_REPLACE_PLACEHOLDER,
        PAGE_READWRITE,
        nullptr, 0
    );

    if (view2 == nullptr) {
        printf ("MapViewOfFile3 failed, error %#x\n", GetLastError());
        goto Exit;
    }

    //
    // Success, return both mapped views to the caller.
    //

    ringBuffer = view1;
    *secondaryView = view2;

    placeholder2 = nullptr;
    view1 = nullptr;
    view2 = nullptr;

Exit:

    if (section != nullptr) {
        CloseHandle (section);
    }

    if (placeholder1 != nullptr) {
        VirtualFree (placeholder1, 0, MEM_RELEASE);
    }

    if (placeholder2 != nullptr) {
        VirtualFree (placeholder2, 0, MEM_RELEASE);
    }

    if (view1 != nullptr) {
        UnmapViewOfFileEx (view1, 0);
    }

    if (view2 != nullptr) {
        UnmapViewOfFileEx (view2, 0);
    }

    return ringBuffer;
}

int __cdecl wmain()
{
    char* ringBuffer;
    void* secondaryView;
    unsigned int bufferSize = 0x10000;

    ringBuffer = (char*) CreateRingBuffer (bufferSize, &secondaryView);

    if (ringBuffer == nullptr) {
        printf ("CreateRingBuffer failed\n");
        return 0;
    }

    //
    // Make sure the buffer wraps properly.
    //

    ringBuffer[0] = 'a';

    if (ringBuffer[bufferSize] == 'a') {
        printf ("The buffer wraps as expected\n");
    }

    UnmapViewOfFile (ringBuffer);
    UnmapViewOfFile (secondaryView);
}

シナリオ 2. メモリを割り当てるときに、優先する NUMA ノードを指定します。


void*
AllocateWithPreferredNode (size_t size, unsigned int numaNode)
{
    MEM_EXTENDED_PARAMETER param = {0};

    param.Type = MemExtendedParameterNumaNode;
    param.ULong = numaNode;

    return VirtualAlloc2 (
        nullptr, nullptr,
        size,
        MEM_RESERVE | MEM_COMMIT,
        PAGE_READWRITE,
        &param, 1);
}

シナリオ 3. 特定の仮想アドレス範囲 (この例では 4 GB 未満) と特定のアラインメントでメモリを割り当てます。


void*
AllocateAlignedBelow2GB (size_t size, size_t alignment)
{
    MEM_ADDRESS_REQUIREMENTS addressReqs = {0};
    MEM_EXTENDED_PARAMETER param = {0};

    addressReqs.Alignment = alignment;
    addressReqs.HighestEndingAddress = (PVOID)(ULONG_PTR) 0x7fffffff;

    param.Type = MemExtendedParameterAddressRequirements;
    param.Pointer = &addressReqs;

    return VirtualAlloc2 (
        nullptr, nullptr,
        size,
        MEM_RESERVE | MEM_COMMIT,
        PAGE_READWRITE,
        &param, 1);
}

要件

   
サポートされている最小のクライアント Windows 10 [デスクトップ アプリのみ]
サポートされている最小のサーバー Windows Server 2016 [デスクトップ アプリのみ]
対象プラットフォーム Windows
ヘッダー memoryapi.h (Windows.h を含む)
Library onecore.lib
[DLL] Kernel32.dll

関連項目

メモリ管理関数

ReadProcessMemory

仮想メモリ関数

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

VirtualQuery

WriteProcessMemory