Compartir a través de


Función VirtualAlloc2 (memoryapi.h)

Reserva, confirma o cambia el estado de una región de memoria dentro del espacio de direcciones virtual de un proceso especificado (la memoria asignada se inicializa en cero).

Sintaxis

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

Identificador de un proceso. La función asigna memoria dentro del espacio de direcciones virtual de este proceso.

El identificador debe tener el PROCESS_VM_OPERATION derecho de acceso. Para obtener más información, consulte Derechos de acceso y seguridad de procesos.

Si Process es NULL, la función asigna memoria para el proceso de llamada.

[in, optional] BaseAddress

Puntero que especifica una dirección inicial deseada para la región de páginas que desea asignar.

Si BaseAddress es null, la función determina dónde asignar la región.

Si BaseAddress no es null, cualquier estructura de MEM_ADDRESS_REQUIREMENTS proporcionada debe constar de todos los ceros y la dirección base debe ser un múltiplo de la granularidad de asignación del sistema. Para determinar la granularidad de asignación, use la función GetSystemInfo.

Si esta dirección está dentro de un enclave que no se ha inicializado llamando a InitializeEnclave, VirtualAlloc2 asigna una página de ceros para el enclave en esa dirección. La página debe no confirmarse previamente y no se medirá con la instrucción EEXTEND del modelo de programación intel Software Guard Extensions.

Si la dirección de dentro de un enclave inicializado, se produce un error en la operación de asignación con el error ERROR_INVALID_ADDRESS. Esto es cierto para enclaves que no admiten la administración dinámica de memoria (es decir, SGX1). Los enclaves SGX2 permitirán la asignación y el enclave debe aceptar la página una vez asignado.

[in] Size

Tamaño de la región de memoria que se va a asignar, en bytes.

El tamaño siempre debe ser un múltiplo del tamaño de página.

Si BaseAddress no es NULL, la función asigna todas las páginas que contienen uno o más bytes en el intervalo desde BaseAddress a BaseAddress+Size. Esto significa, por ejemplo, que un intervalo de 2 bytes que extiende un límite de página hace que la función asigne ambas páginas.

[in] AllocationType

Tipo de asignación de memoria. Este parámetro debe contener uno de los siguientes valores.

Valor Significado
MEM_COMMIT
0x00001000
Asigna cargos de memoria (del tamaño general de la memoria y de los archivos de paginación en el disco) para las páginas de memoria reservadas especificadas. La función también garantiza que cuando el autor de la llamada accede inicialmente a la memoria, el contenido será cero. Las páginas físicas reales no se asignan a menos que se acceda realmente a las direcciones virtuales o hasta que se acceda a ellas.

Para reservar y confirmar páginas en un paso, llame a VirtualAlloc2 con MEM_COMMIT | MEM_RESERVE.

Si se intenta confirmar un intervalo de direcciones específico, se especifica MEM_COMMIT sin MEM_RESERVE y unNULLBaseAddress se produce un error a menos que ya se haya reservado todo el intervalo. El código de error resultante es ERROR_INVALID_ADDRESS.

Un intento de confirmar una página que ya está confirmada no hace que se produzca un error en la función. Esto significa que puede confirmar páginas sin determinar primero el estado de compromiso actual de cada página.

Si BaseAddress especifica una dirección dentro de un enclave, AllocationType debe ser MEM_COMMIT.

MEM_RESERVE
0x00002000
Reserva un intervalo del espacio de direcciones virtuales del proceso sin asignar ningún almacenamiento físico real en memoria o en el archivo de paginación en el disco.

Confirme las páginas reservadas llamando a VirtualAlloc2 de nuevo con MEM_COMMIT. Para reservar y confirmar páginas en un paso, llame a VirtualAlloc2 con MEM_COMMIT | MEM_RESERVE.

Otras funciones de asignación de memoria, como malloc y LocalAlloc, no pueden usar memoria reservada hasta que se haya liberado.

MEM_REPLACE_PLACEHOLDER
0x00004000
Reemplaza un marcador de posición por una asignación privada normal. Solo se admiten vistas de sección con respaldo de datos o pf (sin imágenes, memoria física, etc.). Al reemplazar un marcador de posición, BaseAddress y Size deben coincidir exactamente con los del marcador de posición y cualquier estructura de MEM_ADDRESS_REQUIREMENTS proporcionada debe constar de todos los ceros.

Después de reemplazar un marcador de posición por una asignación privada, para liberar esa asignación a un marcador de posición, consulte el parámetro dwFreeType de VirtualFree y VirtualFreeEx.

Un marcador de posición es un tipo de región de memoria reservada.

MEM_RESERVE_PLACEHOLDER
0x00040000
Para crear un marcador de posición, llame VirtualAlloc2 con MEM_RESERVE | MEM_RESERVE_PLACEHOLDER y PageProtection establezca en PAGE_NOACCESS. Para liberar, dividir o fusionar un marcador de posición, consulte el parámetro dwFreeType de VirtualFree y VirtualFreeEx.

Un marcador de posición es un tipo de región de memoria reservada.

MEM_RESET
0x00080000
Indica que los datos del intervalo de memoria especificados por BaseAddress y Size ya no son de interés. Las páginas no deben leerse ni escribirse en el archivo de paginación. Sin embargo, el bloque de memoria se usará de nuevo más adelante, por lo que no debe descommitido. Este valor no se puede usar con ningún otro valor.

El uso de este valor no garantiza que el intervalo operado con MEM_RESET contendrá ceros. Si desea que el intervalo contenga ceros, descommita la memoria y, a continuación, vuelva a enviarla.

Cuando se usa MEM_RESET, la función VirtualAlloc2 omite el valor de fProtect. Sin embargo, debe establecer fProtect en un valor de protección válido, como PAGE_NOACCESS.

virtualAlloc2 devuelve un error si usa MEM_RESET y el intervalo de memoria se asigna a un archivo. Una vista compartida solo es aceptable si se asigna a un archivo de paginación.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO solo se debe llamar en un intervalo de direcciones al que MEM_RESET se aplicó correctamente anteriormente. Indica que los datos del intervalo de memoria especificado por BaseAddress y Size es de interés para el autor de la llamada e intenta invertir los efectos de MEM_RESET. Si la función se ejecuta correctamente, significa que todos los datos del intervalo de direcciones especificados están intactos. Si se produce un error en la función, al menos algunos de los datos del intervalo de direcciones se han reemplazado por ceros.

Este valor no se puede usar con ningún otro valor. Si se llama a MEM_RESET_UNDO en un intervalo de direcciones que no se MEM_RESET anteriormente, el comportamiento no está definido. Al especificar MEM_RESET, la función VirtualAlloc2 omite el valor de PageProtection. Sin embargo, todavía debe establecer PageProtection en un valor de protección válido, como PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 y Windows XP: La marca de MEM_RESET_UNDO no se admite hasta Windows 8 y Windows Server 2012.

 

Este parámetro también puede especificar los siguientes valores como se indica.

Valor Significado
MEM_LARGE_PAGES
0x20000000
Asigna memoria mediante compatibilidad con páginas grandes.

El tamaño y la alineación deben ser varios de los mínimos de página grande. Para obtener este valor, use la función GetLargePageMinimum.

Si especifica este valor, también debe especificar MEM_RESERVE y MEM_COMMIT.

MEM_64K_PAGES
0x20400000
Sugerencia al sistema operativo para asignar la memoria mediante 64 000 páginas, si es posible.

Una página de 64K es una región de memoria que tiene un tamaño de 64 K, prácticamente y físicamente contiguo, y virtual y físicamente alineada en un límite de 64K.

De forma predeterminada, la memoria asignada mediante MEM_64K_PAGES es paginable y las páginas físicas que respaldan la memoria se asignan a petición (en el momento del acceso). Si la memoria física está demasiado fragmentada para ensamblar una página físicamente contigua de 64 K, en su lugar se puede asignar toda o parte de una asignación de MEM_64K_PAGES mediante páginas pequeñas no contiguas.

Si MEM_64K_PAGES se combina con el atributo MEM_EXTENDED_PARAMETER_NONPAGED, la asignación se asignará mediante páginas 64K no paginadas. En ese caso, si no se pueden obtener páginas contiguas de 64K, se producirá un error en la asignación.

Si se especifica MEM_64K_PAGES, los parámetros Size y BaseAddress deben ser múltiplo de 64K (BaseAddress puede ser NULL).

MEM_PHYSICAL
0x00400000
Reserva un intervalo de direcciones que se puede usar para asignar páginas extensiones de ventana de direcciones (AWE).

Este valor se debe usar con MEM_RESERVE y ningún otro valor.

MEM_TOP_DOWN
0x00100000
Asigna memoria en la dirección más alta posible. Esto puede ser más lento que las asignaciones normales, especialmente cuando hay muchas asignaciones.

[in] PageProtection

Protección de memoria para la región de páginas que se va a asignar. Si se confirman las páginas, puede especificar cualquiera de las constantes de protección de memoria .

Si BaseAddress especifica una dirección dentro de un enclave, PageProtection no puede ser ninguno de los valores siguientes:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Al asignar memoria dinámica para un enclave, el parámetro PageProtection debe ser PAGE_READWRITE o PAGE_EXECUTE_READWRITE.

[in, out, optional] ExtendedParameters

Puntero opcional a uno o varios parámetros extendidos de tipo MEM_EXTENDED_PARAMETER. Cada uno de esos valores de parámetro extendidos puede tener un campo Type de MemExtendedParameterAddressRequirements o MemExtendedParameterNumaNode. Si no se proporciona ningún parámetro extendido MemExtendedParameterNumaNode, el comportamiento es el mismo que para las funciones VirtualAlloc/MapViewOfFile (es decir, el nodo NUMA preferido para las páginas físicas se determina en función del procesador ideal del subproceso que accede primero a la memoria).

[in] ParameterCount

Número de parámetros extendidos a los que apunta ExtendedParameters.

Valor devuelto

Si la función se ejecuta correctamente, el valor devuelto es la dirección base de la región asignada de páginas.

Si se produce un error en la función, el valor devuelto es NULL. Para obtener información de error extendida, llame a GetLastError.

Observaciones

Esta función le permite especificar:

  • un intervalo de espacio de direcciones virtuales y una restricción de alineación de potencia de 2 para las nuevas asignaciones
  • un número arbitrario de parámetros extendidos
  • un nodo NUMA preferido para la memoria física como parámetro extendido (consulte el parámetro ExtendedParameters).
  • una operación de marcador de posición (específicamente, reemplazo).

Esta API proporciona técnicas especializadas para administrar la memoria virtual en compatibilidad con juegos de alto rendimiento y aplicaciones de servidor. Por ejemplo, los marcadores de posición permiten crear particiones explícitas, superponer y reasignar un intervalo de memoria reservado; se puede usar para implementar regiones extensibles arbitrariamente o búferes de anillo de memoria virtual. virtualAlloc2 también permite asignar memoria con una alineación de memoria específica.

Cada página tiene un estado de página de asociado. La función VirtualAlloc2 puede realizar las siguientes operaciones:

  • Confirmación de una región de páginas reservadas
  • Reserva de una región de páginas gratuitas
  • Reservar y confirmar simultáneamente una región de páginas gratuitas

virtualAlloc2 pueden confirmar páginas que ya están confirmadas, pero no pueden reservar páginas que ya están reservadas. Esto significa que puede confirmar un intervalo de páginas, independientemente de si ya se han confirmado y la función no producirá un error. En general, sin embargo, solo se debe especificar un intervalo mínimo de páginas no confirmadas principalmente, ya que confirmar un gran número de páginas que ya están confirmadas puede hacer que la llamada de VirtualAlloc2 tarde mucho más.

Puede usar virtualAlloc2 para reservar un bloque de páginas y, a continuación, realizar llamadas adicionales a VirtualAlloc2 para confirmar páginas individuales desde el bloque reservado. Esto permite que un proceso reserve un intervalo de su espacio de direcciones virtual sin consumir almacenamiento físico hasta que sea necesario.

Si el parámetro de lpAddress no es null, la función usa el lpAddress y dwSize parámetros para calcular la región de las páginas que se van a asignar. El estado actual de todo el intervalo de páginas debe ser compatible con el tipo de asignación especificado por el parámetro flAllocationType. De lo contrario, se produce un error en la función y no se asigna ninguna de las páginas. Este requisito de compatibilidad no impide confirmar una página ya confirmada; consulte la lista anterior.

Para ejecutar código generado dinámicamente, use VirtualAlloc2 para asignar memoria y la función VirtualProtectEx para conceder acceso a PAGE_EXECUTE.

La función virtualAlloc2 de se puede usar para reservar una región de extensiones de ventana de direcciones (AWE) de memoria dentro del espacio de direcciones virtuales de un proceso especificado. Después, esta región de memoria se puede usar para asignar páginas físicas dentro y fuera de la memoria virtual según lo requiera la aplicación. Los valores MEM_PHYSICAL y MEM_RESERVE deben establecerse en el parámetro allocationType de . No se debe establecer el valor de MEM_COMMIT. La protección de páginas debe establecerse en PAGE_READWRITE.

La función VirtualFreeEx puede descommitir una página confirmada, liberar el almacenamiento de la página o puede descommitir y liberar simultáneamente una página confirmada. También puede liberar una página reservada, lo que lo convierte en una página gratuita.

Al crear una región que será ejecutable, el programa de llamada asume la responsabilidad de garantizar la coherencia de caché a través de una llamada adecuada a FlushInstructionCache una vez establecido el código. De lo contrario, los intentos de ejecutar código fuera de la región ejecutable recién pueden producir resultados impredecibles.

Ejemplos

Escenario 1. Cree un búfer circular asignando dos vistas adyacentes de la misma sección de memoria compartida.

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

Escenario 2. Especifique un nodo NUMA preferido al asignar memoria.


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

Escenario 3. Asigne memoria en un intervalo de direcciones virtuales específico (por debajo de 4 GB, en este ejemplo) y con alineación específica.


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
cliente mínimo admitido Windows 10 [solo aplicaciones de escritorio]
servidor mínimo admitido Windows Server 2016 [solo aplicaciones de escritorio]
de la plataforma de destino de Windows
encabezado de memoryapi.h (incluya Windows.h)
biblioteca de onecore.lib
DLL de Kernel32.dll

Consulte también

funciones de administración de memoria

ReadProcessMemory

funciones de memoria virtual

VirtualAllocExNuma

VirtualFreeEx

virtualLock

VirtualProtect

virtualQuery

WriteProcessMemory