Compartilhar via


Função VirtualAlloc2 (memoryapi.h)

Reserva, confirma ou altera o estado de uma região de memória dentro do espaço de endereço virtual de um processo especificado (a memória alocada é inicializada como zero).

Sintaxe

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
);

Parâmetros

[in, optional] Process

O identificador de um processo. A função aloca memória dentro do espaço de endereço virtual desse processo.

O identificador deve ter o acesso PROCESS_VM_OPERATION correto. Para obter mais informações, consulte Process Security and Access Rights.

Se Process for NULL, a função alocará memória para o processo de chamada.

[in, optional] BaseAddress

O ponteiro que especifica um endereço inicial desejado para a região das páginas que você deseja alocar.

Se BaseAddress for NULL, a função determinará onde alocar a região.

Se BaseAddress não for NULL, qualquer estrutura de MEM_ADDRESS_REQUIREMENTS fornecida deverá consistir em todos os zeros e o endereço base deverá ser um múltiplo da granularidade de alocação do sistema. Para determinar a granularidade de alocação, use a função GetSystemInfo.

Se esse endereço estiver dentro de um enclave que você não inicializou chamando InitializeEnclave, VirtualAlloc2 alocará uma página de zeros para o enclave nesse endereço. A página deve ser não confirmada anteriormente e não será medida com a instrução EEXTEND do modelo de programação extensões do Intel Software Guard.

Se o endereço em um enclave que você inicializou, a operação de alocação falhará com o erro ERROR_INVALID_ADDRESS. Isso é verdade para enclaves que não dão suporte ao gerenciamento dinâmico de memória (ou seja, SGX1). Os enclaves SGX2 permitirão a alocação e a página deve ser aceita pelo enclave depois de alocada.

[in] Size

O tamanho da região da memória a ser alocada, em bytes.

O tamanho deve ser sempre um múltiplo do tamanho da página.

Se BaseAddress não for NULL, a função alocará todas as páginas que contêm um ou mais bytes no intervalo de BaseAddress a BaseAddress+Size. Isso significa, por exemplo, que um intervalo de 2 bytes que ultrapassa um limite de página faz com que a função aloque ambas as páginas.

[in] AllocationType

O tipo de alocação de memória. Esse parâmetro deve conter um dos valores a seguir.

Valor Significado
MEM_COMMIT
0x00001000
Aloca encargos de memória (do tamanho geral da memória e dos arquivos de paginação em disco) para as páginas de memória reservadas especificadas. A função também garante que, quando o chamador acessar inicialmente a memória, o conteúdo será zero. As páginas físicas reais não são alocadas, a menos/até que os endereços virtuais sejam realmente acessados.

Para reservar e confirmar páginas em uma etapa, chame VirtualAlloc2 com MEM_COMMIT | MEM_RESERVE.

Tentar confirmar um intervalo de endereços específico especificando MEM_COMMIT sem MEM_RESERVE e umNULLBaseAddress falhar, a menos que todo o intervalo já tenha sido reservado. O código de erro resultante é ERROR_INVALID_ADDRESS.

Uma tentativa de confirmar uma página que já está confirmada não faz com que a função falhe. Isso significa que você pode confirmar páginas sem primeiro determinar o estado de compromisso atual de cada página.

Se BaseAddress especificar um endereço dentro de um enclave, AllocationType deverá ser MEM_COMMIT.

MEM_RESERVE
0x00002000
Reserva um intervalo do espaço de endereço virtual do processo sem alocar nenhum armazenamento físico real na memória ou no arquivo de paginação no disco.

Você confirma páginas reservadas chamando VirtualAlloc2 novamente com MEM_COMMIT. Para reservar e confirmar páginas em uma etapa, chame VirtualAlloc2 com MEM_COMMIT | MEM_RESERVE.

Outras funções de alocação de memória, como malloc e LocalAlloc, não podem usar memória reservada até que ela tenha sido liberada.

MEM_REPLACE_PLACEHOLDER
0x00004000
Substitui um espaço reservado por uma alocação privada normal. Há suporte apenas para exibições de seção com suporte de dados/pf (sem imagens, memória física etc.). Quando você substitui um espaço reservado, BaseAddress e Size deve corresponder exatamente aos do espaço reservado e qualquer estrutura de MEM_ADDRESS_REQUIREMENTS fornecida deve consistir em todos os zeros.

Depois de substituir um espaço reservado por uma alocação privada, para liberar essa alocação de volta para um espaço reservado, consulte o parâmetro dwFreeType de VirtualFree e VirtualFreeEx.

Um espaço reservado é um tipo de região de memória reservada.

MEM_RESERVE_PLACEHOLDER
0x00040000
Para criar um espaço reservado, chame VirtualAlloc2 com e pageprotection definidos como PAGE_NOACCESS. Para liberar/dividir/unir um espaço reservado, consulte o parâmetro dwFreeType de VirtualFree e VirtualFreeEx.

Um espaço reservado é um tipo de região de memória reservada.

MEM_RESET
0x00080000
Indica que os dados no intervalo de memória especificado por BaseAddress e de Tamanho não são mais de interesse. As páginas não devem ser lidas ou gravadas no arquivo de paginação. No entanto, o bloco de memória será usado novamente mais tarde, portanto, ele não deve ser descompactado. Esse valor não pode ser usado com qualquer outro valor.

Usar esse valor não garante que o intervalo operado com MEM_RESET conterá zeros. Se você quiser que o intervalo contenha zeros, descompromisse a memória e, em seguida, recomunha-a.

Quando você usa MEM_RESET, a função VirtualAlloc2 ignora o valor de fProtect. No entanto, você ainda deve definir fProtect para um valor de proteção válido, como PAGE_NOACCESS.

VirtualAlloc2 retornará um erro se você usar MEM_RESET e o intervalo de memória for mapeado para um arquivo. Uma exibição compartilhada só será aceitável se ela for mapeada para um arquivo de paginação.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO só deve ser chamado em um intervalo de endereços ao qual MEM_RESET foi aplicado com êxito anteriormente. Indica que os dados no intervalo de memória especificado pelo BaseAddress e Size é de interesse do chamador e tenta reverter os efeitos de MEM_RESET. Se a função for bem-sucedida, isso significa que todos os dados no intervalo de endereços especificado estão intactos. Se a função falhar, pelo menos alguns dos dados no intervalo de endereços serão substituídos por zeros.

Esse valor não pode ser usado com qualquer outro valor. Se MEM_RESET_UNDO for chamado em um intervalo de endereços que não foi MEM_RESET anteriormente, o comportamento será indefinido. Quando você especifica MEM_RESET, a função VirtualAlloc2 ignora o valor de PageProtection. No entanto, você ainda deve definir PageProtection para um valor de proteção válido, como PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 e Windows XP: O sinalizador MEM_RESET_UNDO não tem suporte até o Windows 8 e o Windows Server 2012.

 

Esse parâmetro também pode especificar os valores a seguir, conforme indicado.

Valor Significado
MEM_LARGE_PAGES
0x20000000
Aloca memória usando de suporte de página grande.

O tamanho e o alinhamento devem ser múltiplos do mínimo de página grande. Para obter esse valor, use a função GetLargePageMinimum.

Se você especificar esse valor, também deverá especificar MEM_RESERVE e MEM_COMMIT.

MEM_64K_PAGES
0x20400000
Uma dica para o sistema operacional mapear a memória usando páginas de 64K, se possível.

Uma página de 64K é uma região de memória com 64 K de tamanho, virtual e fisicamente contígua e virtual e fisicamente alinhada em um limite de 64K.

Por padrão, a memória alocada usando MEM_64K_PAGES é paginável e as páginas físicas que fazem backup da memória são alocadas sob demanda (no momento do acesso). Se a memória física estiver muito fragmentada para montar uma página de 64K fisicamente contígua, toda ou parte de uma alocação de MEM_64K_PAGES poderá ser mapeada usando páginas pequenas não contíguas.

Se MEM_64K_PAGES for combinado com o atributo MEM_EXTENDED_PARAMETER_NONPAGED, a alocação será mapeada usando páginas de 64K não paginadas. Nesse caso, se páginas contíguas de 64K não puderem ser obtidas, a alocação falhará.

Se MEM_64K_PAGES for especificado, os parâmetros Size e BaseAddress deverão ser múltiplos de 64K (BaseAddress pode ser NULL).

MEM_PHYSICAL
0x00400000
Reserva um intervalo de endereços que pode ser usado para mapear páginas do AWE ( de Extensões de Janela de Endereço).

Esse valor deve ser usado com MEM_RESERVE e nenhum outro valor.

MEM_TOP_DOWN
0x00100000
Aloca memória no endereço mais alto possível. Isso pode ser mais lento do que as alocações regulares, especialmente quando há muitas alocações.

[in] PageProtection

A proteção de memória para a região das páginas a ser alocada. Se as páginas estiverem sendo confirmadas, você poderá especificar qualquer uma das constantes de proteção de memória .

Se BaseAddress especificar um endereço dentro de um enclave, PageProtection não poderá ser nenhum dos seguintes valores:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Ao alocar memória dinâmica para um enclave, o parâmetro PageProtection deve ser PAGE_READWRITE ou PAGE_EXECUTE_READWRITE.

[in, out, optional] ExtendedParameters

Um ponteiro opcional para um ou mais parâmetros estendidos do tipo MEM_EXTENDED_PARAMETER. Cada um desses valores de parâmetro estendidos pode ter um campo Type de MemExtendedParameterAddressRequirements ou MemExtendedParameterNumaNode. Se nenhum MemExtendedParameterNumaNode parâmetro estendido for fornecido, o comportamento será o mesmo das funções MapViewOfFile (ou seja, o nó NUMA preferencial para as páginas físicas é determinado com base no processador ideal do thread que primeiro acessa a memória).

[in] ParameterCount

O número de parâmetros estendidos apontados por ExtendedParameters.

Valor de retorno

Se a função for bem-sucedida, o valor retornado será o endereço base da região alocada de páginas.

Se a função falhar, o valor retornado será NULL. Para obter informações de erro estendidas, chame GetLastError.

Observações

Essa função permite que você especifique:

  • um intervalo de espaço de endereço virtual e uma restrição de alinhamento de potência de 2 para novas alocações
  • um número arbitrário de parâmetros estendidos
  • um nó NUMA preferencial para a memória física como um parâmetro estendido (consulte o parâmetro ExtendedParameters)
  • uma operação de espaço reservado (especificamente, substituição).

Essa API fornece técnicas especializadas para gerenciar a memória virtual em suporte a jogos de alto desempenho e aplicativos de servidor. Por exemplo, os espaços reservados permitem que um intervalo de memória reservado seja explicitamente particionado, sobreposto e mapeado novamente; isso pode ser usado para implementar regiões arbitrariamente extensíveis ou buffers de anel de memória virtual. VirtualAlloc2 também permite alocar memória com um alinhamento de memória específico.

Cada página tem um estado de página associado. A função VirtualAlloc2 pode executar as seguintes operações:

  • Confirmar uma região de páginas reservadas
  • Reservar uma região de páginas gratuitas
  • Reservar e confirmar simultaneamente uma região de páginas gratuitas

VirtualAlloc2 podem confirmar páginas que já estão confirmadas, mas não podem reservar páginas que já estão reservadas. Isso significa que você pode confirmar um intervalo de páginas, independentemente de elas já terem sido confirmadas e a função não falhará. No entanto, em geral, apenas um intervalo mínimo de páginas não confirmadas deve ser especificado, pois a confirmação de um grande número de páginas já confirmadas pode fazer com que a chamada VirtualAlloc2 deva muito mais tempo.

Você pode usar VirtualAlloc2 para reservar um bloco de páginas e, em seguida, fazer chamadas adicionais para VirtualAlloc2 para confirmar páginas individuais do bloco reservado. Isso permite que um processo reserve um intervalo de seu espaço de endereço virtual sem consumir armazenamento físico até que seja necessário.

Se o parâmetro lpAddress não for NULL, a função usará os parâmetros lpAddress e dwSize para calcular a região das páginas a serem alocadas. O estado atual de todo o intervalo de páginas deve ser compatível com o tipo de alocação especificado pelo parâmetro flAllocationType. Caso contrário, a função falhará e nenhuma das páginas será alocada. Esse requisito de compatibilidade não impede a confirmação de uma página já confirmada; consulte a lista anterior.

Para executar código gerado dinamicamente, use VirtualAlloc2 para alocar memória e a função VirtualProtectEx para conceder acesso PAGE_EXECUTE.

A função VirtualAlloc2 pode ser usada para reservar uma região de memória de extensões de janela de endereço (AWE) dentro do espaço de endereço virtual de um processo especificado. Essa região de memória pode ser usada para mapear páginas físicas para dentro e para fora da memória virtual, conforme exigido pelo aplicativo. Os valores MEM_PHYSICAL e MEM_RESERVE devem ser definidos no parâmetro AllocationType. O valor MEM_COMMIT não deve ser definido. A proteção de página deve ser definida como PAGE_READWRITE.

A função VirtualFreeEx pode descompromissar uma página confirmada, liberar o armazenamento da página ou descompromissar e liberar simultaneamente uma página confirmada. Ele também pode liberar uma página reservada, tornando-a uma página gratuita.

Ao criar uma região que será executável, o programa de chamada é responsável por garantir a coerência do cache por meio de uma chamada apropriada para FlushInstructionCache depois que o código tiver sido definido em vigor. Caso contrário, as tentativas de executar código fora da região recém-executável podem produzir resultados imprevisíveis.

Exemplos

Cenário 1. Crie um buffer circular mapeando duas exibições adjacentes da mesma seção de memória compartilhada.

#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);
}

Cenário 2. Especifique um nó NUMA preferencial ao alocar memória.


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);
}

Cenário 3. Aloque memória em um intervalo de endereços virtual específico (abaixo de 4 GB, neste exemplo) e com alinhamento específico.


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);
}

Requisitos

Requisito Valor
de cliente com suporte mínimo Windows 10 [somente aplicativos da área de trabalho]
servidor com suporte mínimo Windows Server 2016 [somente aplicativos da área de trabalho]
da Plataforma de Destino Windows
cabeçalho memoryapi.h (inclua Windows.h)
biblioteca onecore.lib
de DLL Kernel32.dll

Consulte também

Funções de gerenciamento de memória

ReadProcessMemory

Funções de memória virtual

VirtualAllocExNuma

VirtualFreeEx

virtualLock

virtualProtect

VirtualQuery

WriteProcessMemory