Share via


VirtualAlloc2 函式 (memoryapi.h)

在所配置記憶體的虛擬位址空間內保留、認可或變更記憶體區域的狀態, (配置的記憶體初始化為零) 。

語法

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,函式會決定要配置區域的位置。

如果 BaseAddress 不是 Null,則任何提供的 MEM_ADDRESS_REQUIREMENTS 結構都必須包含所有零,而且基底位址必須是系統組態細微性的倍數。 若要判斷配置細微性,請使用 GetSystemInfo 函式。

如果此位址位於您未呼叫 InitializeEnclave所初始化的記憶體保護區內, VirtualAlloc2 會為該位址的記憶體保護區配置零頁。 頁面必須先前未認可,且不會使用 Intel Software Guard Extensions 程式設計模型的 EEXTEND 指令來測量。

如果在您初始化的記憶體保護區內位址,則配置作業會失敗,並出現 ERROR_INVALID_ADDRESS 錯誤。 這適用于不支援動態記憶體管理 (的記憶體保護區,也就是 SGX1) 。 SGX2 記憶體保護區將允許配置,且配置後,記憶體保護區必須接受該頁面。

[in] Size

要配置的記憶體區域大小,以位元組為單位。

大小一律必須是頁面大小的倍數。

如果BaseAddress不是Null,函式會配置包含BaseAddress 到 BaseAddress+Size範圍中一或多個位元組的所有頁面。 例如,這表示跨越頁面界限的 2 位元組範圍會導致函式配置這兩個頁面。

[in] AllocationType

記憶體配置的類型。 此參數必須包含下列其中一個值。

意義
MEM_COMMIT
0x00001000
針對指定的保留記憶體頁面,配置記憶體費用 (磁片上的整體大小和分頁檔案) 。 函式也保證當呼叫端稍後一開始存取記憶體時,內容會是零。 除非實際存取虛擬位址,否則不會配置實際的實體頁面。

若要在一個步驟中保留和認可頁面,請使用 呼叫 VirtualAlloc2MEM_COMMIT | MEM_RESERVE

除非已保留整個範圍,否則嘗試藉由指定不MEM_RESERVE MEM_COMMIT認可特定位址範圍,且非NullBaseAddress會失敗。 產生的錯誤碼 ERROR_INVALID_ADDRESS

嘗試認可已認可的頁面,並不會使函式失敗。 這表示您可以認可頁面,而不需要先判斷每個頁面的目前承諾用量狀態。

如果 BaseAddress 指定記憶體保護區內的位址, 則 AllocationType 必須 MEM_COMMIT

MEM_RESERVE
0x00002000
保留進程的虛擬位址空間範圍,而不需在記憶體或磁片上的分頁檔案中配置任何實際的實體儲存體。

您可以使用MEM_COMMIT再次呼叫VirtualAlloc2來認可保留頁面。 若要在一個步驟中保留和認可頁面,請使用 呼叫 VirtualAlloc2MEM_COMMIT | MEM_RESERVE

其他記憶體配置函式,例如 mallocLocalAlloc,在釋放之前,無法使用保留的記憶體。

MEM_REPLACE_PLACEHOLDER
0x00004000
以一般私用配置取代預留位置。 (沒有映射、實體記憶體等) ,僅支援資料/pf 支援的區段檢視。 當您取代預留位置時, BaseAddressSize 必須完全符合預留位置的預留位置,而且任何提供的 MEM_ADDRESS_REQUIREMENTS 結構都必須包含所有零。

將預留位置取代為私用配置之後,若要將該配置釋放回預留位置,請參閱VirtualFree 和 VirtualFreeExdwFreeType參數。

預留位置是保留的記憶體區欄位型別。

MEM_RESERVE_PLACEHOLDER
0x00040000
若要建立預留位置,請呼叫 VirtualAlloc2 ,並將 MEM_RESERVE | MEM_RESERVE_PLACEHOLDERPageProtection 設定為 PAGE_NOACCESS。 若要釋放/分割/聯合預留位置,請參閱VirtualFree 和 VirtualFreeEx 的 dwFreeType參數。

預留位置是保留的記憶體區欄位型別。

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: 在 Windows 8 和 Windows Server 2012 之前,不支援 MEM_RESET_UNDO 旗標。

 

此參數也可以指定下列值,如所述。

意義
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_READWRITEPAGE_EXECUTE_READWRITE

[in, out, optional] ExtendedParameters

MEM_EXTENDED_PARAMETER 類型的一或多個擴充參數的選擇性指標。 這些擴充參數值本身可以有MemExtendedParameterAddressRequirementsMemExtendedParameterNumaNodeType欄位。 如果未提供MemExtendedParameterNumaNode擴充參數,則行為與VirtualAlloc/MapViewOfFile函式 (相同,實體頁面的慣用 NUMA 節點會根據第一次存取記憶體) 執行緒的理想處理器來決定。

[in] ParameterCount

ExtendedParameters所指向的擴充參數數目。

傳回值

如果函式成功,則傳回值是頁面配置區域的基底位址。

如果函式失敗,則傳回值為 Null。 若要取得擴充的錯誤資訊,請呼叫 GetLastError

備註

此函式可讓您指定:

  • 新配置的虛擬位址空間範圍和 2 個對齊能力限制
  • 任意數目的擴充參數
  • 作為擴充參數的實體記憶體慣用 NUMA 節點, (請參閱 ExtendedParameters 參數)
  • 特別 (取代) 預留位置作業。

此 API 提供特殊技術來管理虛擬記憶體,以支援高效能遊戲和伺服器應用程式。 例如,預留位置允許明確分割、重迭和重新對應保留的記憶體範圍;這可用來實作任意擴充的區域或虛擬記憶體通道緩衝區。 VirtualAlloc2 也允許配置具有特定記憶體對齊方式的記憶體。

每個頁面都有相關聯的 頁面狀態VirtualAlloc2函式可以執行下列作業:

  • 認可保留頁面的區域
  • 保留免費頁面的區域
  • 同時保留並認可免費頁面的區域

VirtualAlloc2 可以認可已認可的頁面,但無法保留已保留的頁面。 這表示不論頁面是否已認可,您都可以認可一系列頁面,而且函式不會失敗。 不過,一般而言,應該只指定最少量未認可的頁面範圍,因為認可已認可的大量頁面可能會導致 VirtualAlloc2 呼叫花費更長的時間。

您可以使用 VirtualAlloc2 來保留頁面區塊,然後對 VirtualAlloc2 進行其他呼叫,以認可保留區塊中的個別頁面。 這可讓程式保留其虛擬位址空間的範圍,而不需要耗用實體儲存體,直到需要為止。

如果 lpAddress 參數不是 Null,函式會使用 lpAddressdwSize 參數來計算要配置的頁面區域。 整個頁面範圍的目前狀態必須與 flAllocationType 參數所指定的配置類型相容。 否則,函式會失敗,而且不會配置任何頁面。 此相容性需求不會排除認可已認可的頁面;請參閱上述清單。

若要執行動態產生的程式碼,請使用 VirtualAlloc2 來配置記憶體,並使用 VirtualProtectEx 函式來授 與PAGE_EXECUTE 存取權。

VirtualAlloc2函式可用來在指定進程的虛擬位址空間內,保留 AWE) 記憶體的位址視窗延伸模組 (區域。 然後,您可以使用此記憶體區域,將實體頁面對應至應用程式所需的虛擬記憶體和移出。 MEM_PHYSICAL和MEM_RESERVE值必須在AllocationType參數中設定。 不得設定 MEM_COMMIT 值。 頁面保護必須設定為 PAGE_READWRITE

VirtualFreeEx函式可以取消認可頁面、釋放頁面的儲存體,也可以同時取消認可並釋放認可的頁面。 它也可以釋放保留的頁面,使其成為免費頁面。

建立將可執行檔區域時,呼叫程式會負責在程式碼設定完成後,透過對 FlushInstructionCache 的適當呼叫確保快取共通。 否則,嘗試從新執行的區域執行程式碼可能會產生無法預測的結果。

範例

案例 1. 藉由對應相同共用記憶體區段的兩個相鄰檢視來建立迴圈緩衝區。

#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. 在此範例中,將記憶體配置在特定虛擬位址範圍 (低於 4GB,) 且具有特定對齊方式。


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)
程式庫 onecore.lib
DLL Kernel32.dll

另請參閱

記憶體管理功能

ReadProcessMemory

虛擬記憶體函式

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

VirtualQuery

WriteProcessMemory