다음을 통해 공유


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 액세스 권한이 있어야 합니다. 자세한 내용은 프로세스 보안 및 액세스 권한참조하세요.

Process NULL경우 함수는 호출 프로세스에 대한 메모리를 할당합니다.

[in, optional] BaseAddress

할당하려는 페이지 영역에 대해 원하는 시작 주소를 지정하는 포인터입니다.

BaseAddress NULL경우 함수는 지역을 할당할 위치를 결정합니다.

BaseAddress NULL않으면 제공된 모든 MEM_ADDRESS_REQUIREMENTS 구조체가 모두 0으로 구성되어야 하며 기본 주소는 시스템 할당 세분성의 배수여야 합니다. 할당 세분성을 확인하려면 GetSystemInfo 함수를 사용합니다.

이 주소가 initializeEnclave호출하여 초기화하지 않은 enclave 내에 있는 경우 VirtualAlloc2 해당 주소의 enclave에 대해 0 페이지를 할당합니다. 이 페이지는 이전에 커밋되지 않았어야 하며 Intel Software Guard 확장 프로그래밍 모델의 EEXTEND 명령으로 측정되지 않습니다.

초기화한 enclave 내의 주소가 있으면 할당 작업이 실패하고 ERROR_INVALID_ADDRESS 오류가 발생합니다. 동적 메모리 관리(예: SGX1)를 지원하지 않는 enclave의 경우 마찬가지입니다. SGX2 enclave는 할당을 허용하며, 할당된 후 Enclave에서 페이지를 수락해야 합니다.

[in] Size

할당할 메모리 영역의 크기(바이트)입니다.

크기는 항상 페이지 크기의 배수여야 합니다.

BaseAddress NULL않으면 함수는 BaseAddress부터 BaseAddress크기범위에서 하나 이상의 바이트를 포함하는 모든 페이지를 할당합니다. 예를 들어 페이지 경계를 가로지르는 2 바이트 범위는 함수가 두 페이지를 모두 할당하도록 합니다.

[in] AllocationType

메모리 할당의 유형입니다. 이 매개 변수는 다음 값 중 하나를 포함해야 합니다.

의미
MEM_COMMIT
0x00001000
지정된 예약된 메모리 페이지에 대한 메모리 요금(전체 메모리 크기 및 디스크의 페이징 파일)을 할당합니다. 또한 이 함수는 나중에 호출자가 처음에 메모리에 액세스할 때 콘텐츠가 0이 되도록 보장합니다. 실제 실제 페이지는 가상 주소에 실제로 액세스하지 않는 한 할당되지 않습니다.

한 단계에서 페이지를 예약하고 커밋하려면 MEM_COMMIT | MEM_RESERVE사용하여 VirtualAlloc2 호출합니다.

전체 범위가 이미 예약되지 않은 경우 MEM_RESERVE 없는 MEM_COMMIT 지정하고NULLBaseAddress 지정하여 특정 주소 범위를 커밋하려고 시도하면 실패합니다. 결과 오류 코드가 ERROR_INVALID_ADDRESS.

이미 커밋된 페이지를 커밋하려고 하면 함수가 실패하지 않습니다. 즉, 각 페이지의 현재 약정 상태를 먼저 결정하지 않고도 페이지를 커밋할 수 있습니다.

BaseAddress enclave 내에서 주소를 지정하는 경우 AllocationTypeMEM_COMMIT합니다.

MEM_RESERVE
0x00002000
메모리 또는 디스크의 페이징 파일에 실제 실제 스토리지를 할당하지 않고 프로세스의 가상 주소 공간 범위를 예약합니다.

MEM_COMMITVirtualAlloc2 다시 호출하여 예약된 페이지를 커밋합니다. 한 단계에서 페이지를 예약하고 커밋하려면 MEM_COMMIT | MEM_RESERVE사용하여 VirtualAlloc2 호출합니다.

malloc 및 localAlloc같은 다른 메모리 할당 함수는 릴리스될 때까지 예약된 메모리를 사용할 수 없습니다.

MEM_REPLACE_PLACEHOLDER
0x00004000
자리 표시자를 일반 프라이빗 할당으로 바꿉니다. 데이터/pf 지원 섹션 뷰만 지원됩니다(이미지, 실제 메모리 등은 없음). 자리 표시자를 바꿀 때 BaseAddress크기 자리 표시자의 개체 틀과 정확히 일치해야 하며 제공된 모든 MEM_ADDRESS_REQUIREMENTS 구조체는 모두 0으로 구성되어야 합니다.

자리 표시자를 프라이빗 할당으로 바꾼 후 해당 할당을 자리 표시자로 다시 해제하려면 VirtualFreeVirtualFreeExdwFreeType 매개 변수를 참조하세요.

자리 표시자는 예약된 메모리 영역의 유형입니다.

MEM_RESERVE_PLACEHOLDER
0x00040000
자리 표시자를 만들려면 MEM_RESERVE | MEM_RESERVE_PLACEHOLDERPageProtectionPAGE_NOACCESS설정된 VirtualAlloc2 호출합니다. 자리 표시자를 해제/분할/병합하려면 VirtualFreeVirtualFreeExdwFreeType 매개 변수를 참조하세요.

자리 표시자는 예약된 메모리 영역의 유형입니다.

MEM_RESET
0x00080000
BaseAddress크기 지정한 메모리 범위의 데이터가 더 이상 관심이 없음을 나타냅니다. 페이지를 페이징 파일에서 읽거나 쓸 수 없습니다. 그러나 메모리 블록은 나중에 다시 사용되므로 커밋을 해제해서는 안 됩니다. 이 값은 다른 값과 함께 사용할 수 없습니다.

이 값을 사용하면 MEM_RESET 함께 작동하는 범위에 0이 포함되지 않습니다. 범위에 0이 포함되도록 하려면 메모리를 커밋 해제한 다음 다시 커밋합니다.

MEM_RESET사용하면 VirtualAlloc2 함수는 fProtect값을 무시합니다. 그러나 fProtectPAGE_NOACCESS같은 유효한 보호 값으로 설정해야 합니다.

VirtualAlloc2MEM_RESET 사용하고 메모리 범위가 파일에 매핑되는 경우 오류를 반환합니다. 공유 뷰는 페이징 파일에 매핑된 경우에만 허용됩니다.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDOMEM_RESET 이전에 성공적으로 적용된 주소 범위에서만 호출해야 합니다. 이는 BaseAddressSize 지정된 메모리 범위의 데이터가 호출자에게 관심을 가지며 MEM_RESET효과를 되돌리려고 시도했음을 나타냅니다. 함수가 성공하면 지정된 주소 범위의 모든 데이터가 그대로 유지됩니다. 함수가 실패하면 주소 범위의 일부 데이터가 0으로 바뀌었습니다.

이 값은 다른 값과 함께 사용할 수 없습니다. 이전에 MEM_RESET 않은 주소 범위에서 MEM_RESET_UNDO 호출되면 동작이 정의되지 않습니다. MEM_RESET지정하면 VirtualAlloc2 함수는 PageProtection값을 무시합니다. 그러나 여전히 PageProtectionPAGE_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_RESERVE 지정하고 MEM_COMMIT합니다.

MEM_64K_PAGES
0x20400000
가능한 경우 64K 페이지를 사용하여 메모리를 매핑하는 운영 체제에 대한 힌트입니다.

64K 페이지는 64K 크기의 메모리 영역으로, 가상 및 물리적으로 연속되며 64K 경계에 사실상 물리적으로 정렬됩니다.

기본적으로 MEM_64K_PAGES 사용하여 할당된 메모리는 페이징 가능하며 메모리를 지원하는 실제 페이지는 액세스 시 필요에 따라 할당됩니다. 물리적 메모리가 너무 조각화되어 물리적으로 연속된 64K 페이지를 어셈블할 수 없는 경우 MEM_64K_PAGES 할당의 전부 또는 일부가 인접하지 않은 작은 페이지를 사용하여 매핑될 수 있습니다.

MEM_64K_PAGES MEM_EXTENDED_PARAMETER_NONPAGED 특성과 결합된 경우 할당은 페이징되지 않은 64K 페이지를 사용하여 매핑됩니다. 이 경우 연속 64K 페이지를 가져올 수 없는 경우 할당이 실패합니다.

MEM_64K_PAGES 지정한 경우 Size 및 BaseAddress 매개 변수는 모두 64K의 배수여야 합니다(BaseAddress는 NULL일 수 있음).

MEM_PHYSICAL
0x00400000
AWE(주소 창 확장) 페이지를 매핑하는 데 사용할 수 있는 주소 범위를 예약합니다.

이 값은 MEM_RESERVE 함께 사용해야 하며 다른 값은 사용하지 않아야 합니다.

MEM_TOP_DOWN
0x00100000
가능한 가장 높은 주소로 메모리를 할당합니다. 이는 특히 할당이 많은 경우 일반 할당보다 느려질 수 있습니다.

[in] PageProtection

할당할 페이지 영역에 대한 메모리 보호입니다. 페이지가 커밋되는 경우메모리 보호 상수 중 하나를 지정할 수 있습니다.

BaseAddress enclave 내에서 주소를 지정하는 경우 PageProtection 다음 값이 될 수 없습니다.

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

enclave에 동적 메모리를 할당할 때 PageProtection 매개 변수는 PAGE_READWRITE 또는 PAGE_EXECUTE_READWRITE합니다.

[in, out, optional] ExtendedParameters

MEM_EXTENDED_PARAMETER형식의 하나 이상의 확장 매개 변수에 대한 선택적 포인터입니다. 이러한 각 확장 매개 변수 값 자체에는 MemExtendedParameterAddressRequirements 또는 MemExtendedParameterNumaNode형식 필드가 있을 수 있습니다. 확장 매개 변수를 MemExtendedParameterNumaNode가 제공되지 않는 경우 동작은 VirtualAllocMapViewOfFile 함수와 동일합니다(즉, 실제 페이지의 기본 NUMA 노드는 먼저 메모리에 액세스하는 스레드의 이상적인 프로세서에 따라 결정됨).

[in] ParameterCount

extendedParameters가리키는 확장 매개 변수 수입니다.

반환 값

함수가 성공하면 반환 값은 할당된 페이지 영역의 기본 주소입니다.

함수가 실패하면 반환 값은 NULL. 확장 오류 정보를 얻으려면 GetLastError호출합니다.

발언

이 함수를 사용하면 다음을 지정할 수 있습니다.

  • 새 할당에 대한 다양한 가상 주소 공간 및 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. 동일한 공유 메모리 섹션의 인접한 두 뷰를 매핑하여 순환 버퍼를 만듭니다.

#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