Funzione VirtualAlloc2 (memoryapi.h)

Riserva, commit o modifica lo stato di un'area di memoria all'interno dello spazio indirizzi virtuale di un processo specificato (la memoria allocata viene inizializzata su zero).

Sintassi

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

Parametri

[in, optional] Process

Handle a un processo. La funzione alloca la memoria all'interno dello spazio indirizzi virtuale di questo processo.

L'handle deve avere il diritto di accesso PROCESS_VM_OPERATION . Per altre informazioni, vedere Elaborare diritti di sicurezza e accesso.

Se Process è NULL, la funzione alloca memoria per il processo chiamante.

[in, optional] BaseAddress

Puntatore che specifica un indirizzo iniziale desiderato per l'area delle pagine da allocare.

Se BaseAddress è NULL, la funzione determina dove allocare l'area.

Se BaseAddress non è NULL, qualsiasi struttura MEM_ADDRESS_REQUIREMENTS fornita deve essere costituita da tutti gli zero e l'indirizzo di base deve essere un multiplo della granularità dell'allocazione del sistema. Per determinare la granularità dell'allocazione, usare la funzione GetSystemInfo .

Se questo indirizzo si trova all'interno di un enclave che non è stato inizializzato chiamando InitializeEnclave, VirtualAlloc2 alloca una pagina di zero per l'enclave a tale indirizzo. La pagina deve essere annullata in precedenza e non verrà misurata con l'istruzione EEXTEND del modello di programmazione Intel Software Guard Extensions.

Se l'indirizzo all'interno di un enclave inizializzato, l'operazione di allocazione ha esito negativo con l'errore di ERROR_INVALID_ADDRESS . Vale per le enclave che non supportano la gestione dinamica della memoria (ad esempio SGX1). Le enclave SGX2 consentono l'allocazione e la pagina deve essere accettata dall'enclave dopo che è stata allocata.

[in] Size

Dimensioni dell'area di memoria da allocare, in byte.

Le dimensioni devono essere sempre un multiplo delle dimensioni della pagina.

Se BaseAddress non è NULL, la funzione alloca tutte le pagine che contengono uno o più byte nell'intervallo compreso tra BaseAddress e BaseAddress+Size. Ciò significa, ad esempio, che un intervallo a 2 byte che straddlea un limite di pagina causa l'allocazione di entrambe le pagine della funzione.

[in] AllocationType

Tipo di allocazione della memoria. Questo parametro deve contenere uno dei valori seguenti.

Valore Significato
MEM_COMMIT
0x00001000
Alloca i costi di memoria (dalla dimensione complessiva della memoria e dai file di paging su disco) per le pagine di memoria riservate specificate. La funzione garantisce anche che quando il chiamante accede inizialmente alla memoria, il contenuto sarà zero. Le pagine fisiche effettive non vengono allocate a meno che non vengano effettivamente accessibili gli indirizzi virtuali.

Per riservare e eseguire il commit di pagine in un unico passaggio, chiamare VirtualAlloc2 con MEM_COMMIT | MEM_RESERVE.

Il tentativo di eseguire il commit di un intervallo di indirizzi specifico specificando MEM_COMMIT senza MEM_RESERVE e un oggetto BaseAddress non NULL non riesce a meno che l'intero intervallo non sia già stato riservato. Il codice di errore risultante è ERROR_INVALID_ADDRESS.

Un tentativo di commit di una pagina già commit non causa l'esito negativo della funzione. Ciò significa che è possibile eseguire il commit di pagine senza prima determinare lo stato di impegno corrente di ogni pagina.

Se BaseAddress specifica un indirizzo all'interno di un enclave, AllocationType deve essere MEM_COMMIT.

MEM_RESERVE
0x00002000
Riserva un intervallo di spazio indirizzi virtuale del processo senza allocare alcuna risorsa di archiviazione fisica effettiva in memoria o nel file di paging su disco.

Si esegue il commit di pagine riservate chiamando di nuovo VirtualAlloc2 con MEM_COMMIT. Per riservare e eseguire il commit di pagine in un unico passaggio, chiamare VirtualAlloc2 con MEM_COMMIT | MEM_RESERVE.

Altre funzioni di allocazione della memoria, ad esempio malloc e LocalAlloc, non possono usare memoria riservata finché non è stata rilasciata.

MEM_REPLACE_PLACEHOLDER
0x00004000
Sostituisce un segnaposto con una normale allocazione privata. Sono supportate solo le visualizzazioni della sezione data/pf-back (nessuna immagine, memoria fisica e così via). Quando si sostituisce un segnaposto, BaseAddress e Size devono corrispondere esattamente a quelli del segnaposto e qualsiasi MEM_ADDRESS_REQUIREMENTS struttura specificata deve essere costituita da tutti gli zero.

Dopo aver sostituito un segnaposto con un'allocazione privata, per liberare tale allocazione a un segnaposto, vedere il parametro dwFreeType di VirtualFree e VirtualFreeEx.

Un segnaposto è un tipo di area di memoria riservata.

MEM_RESERVE_PLACEHOLDER
0x00040000
Per creare un segnaposto, chiamare VirtualAlloc2 con MEM_RESERVE | MEM_RESERVE_PLACEHOLDER e PageProtection impostato su PAGE_NOACCESS. Per liberare/dividere/coalesce un segnaposto, vedere il parametro dwFreeType di VirtualFree e VirtualFreeEx.

Un segnaposto è un tipo di area di memoria riservata.

MEM_RESET
0x00080000
Indica che i dati nell'intervallo di memoria specificati da BaseAddress e Size non sono più interessati. Le pagine non devono essere lette o scritte nel file di paging. Tuttavia, il blocco di memoria verrà usato di nuovo in un secondo momento, quindi non deve essere decommesso. Questo valore non può essere usato con qualsiasi altro valore.

L'uso di questo valore non garantisce che l'intervallo gestito con MEM_RESET contenga zero. Se si vuole che l'intervallo contenga zero, decommettere la memoria e quindi ricommetterla.

Quando si usa MEM_RESET, la funzione VirtualAlloc2 ignora il valore di fProtect. È tuttavia necessario impostare fProtect su un valore di protezione valido, ad esempio PAGE_NOACCESS.

VirtualAlloc2 restituisce un errore se si usa MEM_RESET e l'intervallo di memoria viene mappato a un file. Una visualizzazione condivisa è accettabile solo se viene eseguito il mapping a un file di paging.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO deve essere chiamato solo in un intervallo di indirizzi a cui MEM_RESET è stato applicato correttamente in precedenza. Indica che i dati nell'intervallo di memoria specificato da BaseAddress e Size sono di interesse per il chiamante e tentano di invertire gli effetti di MEM_RESET. Se la funzione ha esito positivo, significa che tutti i dati nell'intervallo di indirizzi specificato sono intatti. Se la funzione ha esito negativo, almeno alcuni dei dati nell'intervallo di indirizzi sono stati sostituiti con zero.

Questo valore non può essere usato con qualsiasi altro valore. Se MEM_RESET_UNDO viene chiamato su un intervallo di indirizzi non MEM_RESET in precedenza, il comportamento non è definito. Quando si specifica MEM_RESET, la funzione VirtualAlloc2 ignora il valore di PageProtection. È tuttavia necessario impostare PageProtection su un valore di protezione valido, ad esempio PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 e Windows XP: Il flag MEM_RESET_UNDO non è supportato fino a Windows 8 e Windows Server 2012.

 

Questo parametro può anche specificare i valori seguenti, come indicato.

Valore Significato
MEM_LARGE_PAGES
0x20000000
Alloca la memoria usando il supporto di pagine di grandi dimensioni.

Le dimensioni e l'allineamento devono essere un multiplo del minimo di pagina di grandi dimensioni. Per ottenere questo valore, usare la funzione GetLargePageMinimum .

Se si specifica questo valore, è necessario specificare anche MEM_RESERVE e MEM_COMMIT.

MEM_PHYSICAL
0x00400000
Riserva un intervallo di indirizzi che può essere usato per mappare le pagine AWE (Address Windowing Extensions ).

Questo valore deve essere usato con MEM_RESERVE e nessun altro valore.

MEM_TOP_DOWN
0x00100000
Alloca la memoria al massimo indirizzo possibile. Questo può essere più lento rispetto alle allocazioni regolari, soprattutto quando sono presenti molte allocazioni.

[in] PageProtection

Protezione della memoria per l'area delle pagine da allocare. Se le pagine vengono eseguite con commit, è possibile specificare una delle costanti di protezione della memoria.

Se BaseAddress specifica un indirizzo all'interno di un enclave, PageProtection non può essere uno dei valori seguenti:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Quando si assegna memoria dinamica per un enclave, il parametro PageProtection deve essere PAGE_READWRITE o PAGE_EXECUTE_READWRITE.

[in, out, optional] ExtendedParameters

Puntatore facoltativo a uno o più parametri estesi di tipo MEM_EXTENDED_PARAMETER. Ognuno di questi valori di parametro estesi può avere un campo Type di MemExtendedParameterAddressRequirements o MemExtendedParameterNumaNode. Se non viene fornito alcun parametro esteso MemExtendedParameterNumaNode , il comportamento è lo stesso per le funzioni VirtualAlloc/MapViewOfFile , ovvero il nodo NUMA preferito per le pagine fisiche viene determinato in base al processore ideale del thread che accede prima alla memoria.

[in] ParameterCount

Numero di parametri estesi puntati da ExtendedParameters.

Valore restituito

Se la funzione ha esito positivo, il valore restituito è l'indirizzo di base dell'area allocata delle pagine.

Se la funzione ha esito negativo, il valore restituito è NULL. Per informazioni dettagliate sull'errore, chiamare GetLastError.

Commenti

Questa funzione consente di specificare:

  • un intervallo di spazio indirizzi virtuale e una restrizione di allineamento power-of-2 per le nuove allocazioni
  • numero arbitrario di parametri estesi
  • un nodo NUMA preferito per la memoria fisica come parametro esteso (vedere il parametro ExtendedParameters )
  • un'operazione segnaposto (specificamente, sostituzione).

Questa API offre tecniche specializzate per la gestione della memoria virtuale a supporto di giochi e applicazioni server ad alte prestazioni. Ad esempio, i segnaposto consentono di partizionare in modo esplicito un intervallo di memoria riservato, sovrapponerlo e ri mappato; questo può essere usato per implementare aree arbitrariamente estendibili o buffer di anello di memoria virtuale. VirtualAlloc2 consente anche di allocare memoria con un allineamento di memoria specifico.

Ogni pagina ha uno stato di pagina associato. La funzione VirtualAlloc2 può eseguire le operazioni seguenti:

  • Eseguire il commit di un'area di pagine riservate
  • Riservare un'area di pagine gratuite
  • Riservare e eseguire il commit simultaneo di un'area di pagine gratuite

VirtualAlloc2 può eseguire il commit di pagine già sottoposte a commit, ma non può riservare pagine già riservate. Ciò significa che è possibile eseguire il commit di un intervallo di pagine, indipendentemente dal fatto che siano già stati sottoposti a commit e la funzione non avrà esito negativo. In generale, tuttavia, è consigliabile specificare solo un intervallo minimo di pagine non inviate, perché il commit di un numero elevato di pagine già sottoposte a commit può richiedere molto più tempo alla chiamata VirtualAlloc2 .

È possibile usare VirtualAlloc2 per riservare un blocco di pagine e quindi effettuare chiamate aggiuntive a VirtualAlloc2 per eseguire il commit di singole pagine dal blocco riservato. Ciò consente a un processo di riservare un intervallo dello spazio indirizzi virtuale senza usare l'archiviazione fisica fino a quando non è necessario.

Se il parametro lpAddress non è NULL, la funzione usa i parametri lpAddress e dwSize per calcolare l'area delle pagine da allocare. Lo stato corrente dell'intero intervallo di pagine deve essere compatibile con il tipo di allocazione specificato dal parametro flAllocationType . In caso contrario, la funzione ha esito negativo e nessuno delle pagine viene allocato. Questo requisito di compatibilità non impedisce il commit di una pagina già commit; vedere l'elenco precedente.

Per eseguire codice generato in modo dinamico, usare VirtualAlloc2 per allocare memoria e la funzione VirtualProtectEx per concedere l'accesso PAGE_EXECUTE .

La funzione VirtualAlloc2 può essere usata per riservare un'area AWE ( Address Windowing Extensions ) della memoria all'interno dello spazio indirizzi virtuale di un processo specificato. Questa area di memoria può quindi essere usata per eseguire il mapping di pagine fisiche in e fuori memoria virtuale, come richiesto dall'applicazione. I valori MEM_PHYSICAL e MEM_RESERVE devono essere impostati nel parametro AllocationType . Il valore MEM_COMMIT non deve essere impostato. La protezione della pagina deve essere impostata su PAGE_READWRITE.

La funzione VirtualFreeEx può decommettere una pagina commit, rilasciare l'archiviazione della pagina o rilasciare contemporaneamente una pagina di commit. Può anche rilasciare una pagina riservata, rendendola una pagina gratuita.

Quando si crea un'area eseguibile, il programma chiamante è responsabile della coesistenza della cache tramite una chiamata appropriata a FlushInstructionCache dopo aver impostato il codice. In caso contrario, i tentativi di esecuzione del codice dall'area appena eseguibile possono produrre risultati imprevedibili.

Esempio

Scenario 1. Creare un buffer circolare mappando due viste adiacenti della stessa sezione di memoria condivisa.

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

Scenario 2. Specificare un nodo NUMA preferito durante l'allocazione della 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);
}

Scenario 3. Allocare memoria in un intervallo di indirizzi virtuali specifico (sotto 4 GB, in questo esempio) e con allineamento specifico.


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

Requisiti

   
Client minimo supportato Windows 10 [solo app desktop]
Server minimo supportato Windows Server 2016 [solo app desktop]
Piattaforma di destinazione Windows
Intestazione memoryapi.h (include Windows.h)
Libreria onecore.lib
DLL Kernel32.dll

Vedere anche

Funzioni di gestione della memoria

ReadProcessMemory

Funzioni di memoria virtuale

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

VirtualQuery

WriteProcessMemory