VirtualAlloc2-Funktion (memoryapi.h)

Reserviert, committet oder ändert den Zustand einer Speicherregion innerhalb des virtuellen Adressraums eines angegebenen Prozesses (zugeordneter Arbeitsspeicher wird auf Null initialisiert).

Syntax

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

Parameter

[in, optional] Process

Das Handle für einen Prozess. Die Funktion ordnet Arbeitsspeicher innerhalb des virtuellen Adressraums dieses Prozesses zu.

Das Handle muss über das zugriffsrecht PROCESS_VM_OPERATION verfügen. Weitere Informationen finden Sie unter Prozesssicherheit und Zugriffsrechte.

Wenn ProcessNULL ist, weist die Funktion Arbeitsspeicher für den aufrufenden Prozess zu.

[in, optional] BaseAddress

Der Zeiger, der eine gewünschte Startadresse für den Bereich der Seiten angibt, die Sie zuordnen möchten.

Wenn BaseAddressNULL ist, bestimmt die Funktion, wo die Region zugeordnet werden soll.

Wenn BaseAddress nicht NULL ist, muss jede bereitgestellte MEM_ADDRESS_REQUIREMENTS-Struktur aus allen Nullen bestehen, und die Basisadresse muss ein Vielfaches der Granularität der Systemzuordnung sein. Verwenden Sie die GetSystemInfo-Funktion , um die Zuordnungsgranularität zu bestimmen.

Wenn sich diese Adresse innerhalb einer Enclave befindet, die Sie nicht durch Aufrufen von InitializeEnclave initialisiert haben, weist VirtualAlloc2 eine Seite mit Nullen für die Enclave an dieser Adresse zu. Die Seite muss zuvor nicht festgelegt sein und wird nicht mit der EEXTEND-Anweisung des Intel Software Guard Extensions-Programmiermodells gemessen.

Wenn die Adresse in einer Enclave, die Sie initialisiert haben, fehlschlägt der Zuordnungsvorgang mit dem ERROR_INVALID_ADDRESS Fehler. Dies gilt für Enclaves, die keine dynamische Speicherverwaltung unterstützen (z. B. SGX1). SGX2-Enclaves erlauben die Zuordnung, und die Seite muss von der Enclave akzeptiert werden, nachdem sie zugewiesen wurde.

[in] Size

Die Größe des zuzuordnenden Speicherbereichs in Bytes.

Die Größe muss immer ein Vielfaches der Seitengröße sein.

Wenn BaseAddress nicht NULL ist, ordnet die Funktion alle Seiten zu, die ein oder mehrere Bytes im Bereich von BaseAddress bis BaseAddress+Size enthalten. Dies bedeutet beispielsweise, dass ein 2-Byte-Bereich, der eine Seitengrenze überspannt, dazu führt, dass die Funktion beide Seiten zuordnet.

[in] AllocationType

Der Typ der Speicherbelegung. Dieser Parameter muss einen der folgenden Werte enthalten.

Wert Bedeutung
MEM_COMMIT
0x00001000
Ordnet Arbeitsspeichergebühren (aus der Gesamtgröße des Arbeitsspeichers und den Auslagerungsdateien auf dem Datenträger) für die angegebenen reservierten Speicherseiten zu. Die Funktion garantiert auch, dass der Inhalt null ist, wenn der Aufrufer später zunächst auf den Speicher zugreift. Tatsächliche physische Seiten werden nur zugeordnet, wenn/bis auf die virtuellen Adressen tatsächlich zugegriffen wird.

Um Seiten in einem Schritt zu reservieren und zu committen, rufen Sie VirtualAlloc2 mit MEM_COMMIT | MEM_RESERVEauf.

Der Versuch, einen bestimmten Adressbereich durch Angabe von MEM_COMMIT ohne MEM_RESERVE und einer BaseAddress ohne NULL zu committen, schlägt fehl, es sei denn, der gesamte Bereich wurde bereits reserviert. Der resultierende Fehlercode ist ERROR_INVALID_ADDRESS.

Ein Commit für eine Seite, die bereits committet wurde, führt nicht dazu, dass die Funktion fehlschlägt. Dies bedeutet, dass Sie Seiten committen können, ohne zuerst den aktuellen Verpflichtungsstatus jeder Seite zu ermitteln.

Wenn BaseAddress eine Adresse innerhalb einer Enclave angibt, muss AllocationTypeMEM_COMMIT sein.

MEM_RESERVE
0x00002000
Reserviert einen Bereich des virtuellen Adressraums des Prozesses, ohne den tatsächlichen physischen Speicher im Arbeitsspeicher oder in der Auslagerungsdatei auf dem Datenträger zuzuweisen.

Sie committen reservierte Seiten, indem Sie VirtualAlloc2 erneut mit MEM_COMMIT aufrufen. Um Seiten in einem Schritt zu reservieren und zu committen, rufen Sie VirtualAlloc2 mit MEM_COMMIT | MEM_RESERVEauf.

Andere Speicherbelegungsfunktionen wie malloc und LocalAlloc können reservierten Arbeitsspeicher erst verwenden, wenn er freigegeben wurde.

MEM_REPLACE_PLACEHOLDER
0x00004000
Ersetzt einen Platzhalter durch eine normale private Zuordnung. Es werden nur daten-/pf-gesicherte Abschnittsansichten unterstützt (keine Bilder, physischer Arbeitsspeicher usw.). Wenn Sie einen Platzhalter ersetzen, müssen BaseAddress und Size genau mit denen des Platzhalters übereinstimmen, und alle bereitgestellten MEM_ADDRESS_REQUIREMENTS Struktur müssen aus allen Nullen bestehen.

Nachdem Sie einen Platzhalter durch eine private Zuordnung ersetzt haben, lesen Sie den dwFreeType-Parameter von VirtualFree undVirtualFreeEx, um diese Zuordnung wieder an einen Platzhalter frei zu geben.

Ein Platzhalter ist ein Typ des reservierten Speicherbereichs.

MEM_RESERVE_PLACEHOLDER
0x00040000
Um einen Platzhalter zu erstellen, rufen Sie VirtualAlloc2 auf, wobei MEM_RESERVE | MEM_RESERVE_PLACEHOLDERpageProtection auf PAGE_NOACCESS festgelegt ist. Informationen zum Freigeben/Teilen/Zusammenfügen eines Platzhalters finden Sie im dwFreeType-Parameter von VirtualFree und VirtualFreeEx.

Ein Platzhalter ist ein Typ des reservierten Speicherbereichs.

MEM_RESET
0x00080000
Gibt an, dass Daten im durch BaseAddress und Size angegebenen Arbeitsspeicherbereich nicht mehr von Interesse sind. Die Seiten sollten nicht aus der Auslagerungsdatei gelesen oder in diese geschrieben werden. Der Speicherblock wird jedoch später wieder verwendet, sodass er nicht decommittediert werden sollte. Dieser Wert kann nicht mit anderen Werten verwendet werden.

Die Verwendung dieses Werts garantiert nicht, dass der mit MEM_RESET betriebene Bereich Nullen enthält. Wenn der Bereich Nullen enthalten soll, heben Sie den Arbeitsspeicher auf, und fügen Sie ihn dann erneut hinzu.

Wenn Sie MEM_RESET verwenden, ignoriert die VirtualAlloc2-Funktion den Wert von fProtect. Sie müssen fProtect jedoch weiterhin auf einen gültigen Schutzwert festlegen, z. B. PAGE_NOACCESS.

VirtualAlloc2 gibt einen Fehler zurück, wenn Sie MEM_RESET verwenden und der Speicherbereich einer Datei zugeordnet ist. Eine freigegebene Ansicht ist nur zulässig, wenn sie einer Auslagerungsdatei zugeordnet ist.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO sollte nur für einen Adressbereich aufgerufen werden, auf den MEM_RESET zuvor erfolgreich angewendet wurde. Es gibt an, dass die Daten im angegebenen Speicherbereich, die durch BaseAddress und Size angegeben werden, für den Aufrufer von Interesse sind, und versucht, die Auswirkungen der MEM_RESET umzukehren. Wenn die Funktion erfolgreich ist, bedeutet dies, dass alle Daten im angegebenen Adressbereich intakt sind. Wenn die Funktion fehlschlägt, wurden zumindest einige der Daten im Adressbereich durch Nullen ersetzt.

Dieser Wert kann nicht mit anderen Werten verwendet werden. Wenn MEM_RESET_UNDO für einen Adressbereich aufgerufen wird, der zuvor nicht MEM_RESET war, ist das Verhalten nicht definiert. Wenn Sie MEM_RESET angeben, ignoriert die VirtualAlloc2-Funktion den Wert von PageProtection. Sie müssen PageProtection jedoch weiterhin auf einen gültigen Schutzwert festlegen, z. B. PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 und Windows XP: Das flag MEM_RESET_UNDO wird erst unter Windows 8 und Windows Server 2012 unterstützt.

 

Dieser Parameter kann auch die folgenden Werte angeben, wie angegeben.

Wert Bedeutung
MEM_LARGE_PAGES
0x20000000
Ordnet Arbeitsspeicher mithilfe von Unterstützung für große Seiten zu.

Größe und Ausrichtung müssen ein Vielfaches des großformatigen Minimums sein. Verwenden Sie zum Abrufen dieses Werts die GetLargePageMinimum-Funktion .

Wenn Sie diesen Wert angeben, müssen Sie auch MEM_RESERVE und MEM_COMMIT angeben.

MEM_PHYSICAL
0x00400000
Reserviert einen Adressbereich, der zum Zuordnen von AWE-Seiten ( Address Windowing Extensions ) verwendet werden kann.

Dieser Wert muss mit MEM_RESERVE und keinem anderen Wert verwendet werden.

MEM_TOP_DOWN
0x00100000
Ordnet Arbeitsspeicher mit der höchstmöglichen Adresse zu. Dies kann langsamer sein als reguläre Zuordnungen, insbesondere wenn viele Zuordnungen vorhanden sind.

[in] PageProtection

Der Speicherschutz für die Region der zuzuordnenden Seiten. Wenn die Seiten committet werden, können Sie eine der Speicherschutzkonstanten angeben.

Wenn BaseAddress eine Adresse innerhalb einer Enclave angibt, kann PageProtection keinen der folgenden Werte sein:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Beim Zuweisen von dynamischem Arbeitsspeicher für eine Enclave muss der PageProtection-ParameterPAGE_READWRITE oder PAGE_EXECUTE_READWRITE sein.

[in, out, optional] ExtendedParameters

Ein optionaler Zeiger auf einen oder mehrere erweiterte Parameter vom Typ MEM_EXTENDED_PARAMETER. Jeder dieser erweiterten Parameterwerte kann selbst ein Type-Feld mit MemExtendedParameterAddressRequirements oder MemExtendedParameterNumaNode aufweisen. Wenn kein erweiterter MemExtendedParameterNumaNode-Parameter angegeben wird, ist das Verhalten identisch mit den VirtualAlloc/MapViewOfFile-Funktionen (das heißt, der bevorzugte NUMA-Knoten für die physischen Seiten wird basierend auf dem idealen Prozessor des Threads bestimmt, der zuerst auf den Arbeitsspeicher zugreift).

[in] ParameterCount

Die Anzahl der erweiterten Parameter, auf die von ExtendedParameters verwiesen wird.

Rückgabewert

Wenn die Funktion erfolgreich ist, ist der Rückgabewert die Basisadresse des zugeordneten Bereichs der Seiten.

Wenn bei der Funktion ein Fehler auftritt, ist der Rückgabewert NULL. Um erweiterte Fehlerinformationen zu erhalten, rufen Sie GetLastError auf.

Bemerkungen

Mit dieser Funktion können Sie Folgendes angeben:

  • ein Bereich des virtuellen Adressraums und eine Power-of-2-Ausrichtungseinschränkung für neue Zuordnungen
  • eine beliebige Anzahl erweiterter Parameter
  • ein bevorzugter NUMA-Knoten für den physischen Arbeitsspeicher als erweiterter Parameter (siehe ExtendedParameters-Parameter )
  • ein Platzhaltervorgang (insbesondere ersetzung).

Diese API bietet spezielle Techniken für die Verwaltung des virtuellen Arbeitsspeichers zur Unterstützung von Hochleistungsspielen und Serveranwendungen. Platzhalter ermöglichen es beispielsweise, dass ein reservierter Speicherbereich explizit partitioniert, überlagert und neu zugeordnet wird. dies kann verwendet werden, um beliebig erweiterbare Regionen oder virtuelle Speicherringpuffer zu implementieren. VirtualAlloc2 ermöglicht auch die Zuweisung von Arbeitsspeicher mit einer bestimmten Speicherausrichtung.

Jede Seite hat einen zugeordneten Seitenzustand. Die VirtualAlloc2-Funktion kann die folgenden Vorgänge ausführen:

  • Commit für eine Region mit reservierten Seiten
  • Reservieren einer Region mit kostenlosen Seiten
  • Gleichzeitige Reservierung und Commit für eine Region mit kostenlosen Seiten

VirtualAlloc2 kann Seiten committen, die bereits committet wurden, aber keine Seiten reservieren, die bereits reserviert sind. Dies bedeutet, dass Sie einen Bereich von Seiten committen können, unabhängig davon, ob sie bereits committet wurden, und die Funktion nicht fehlschlägt. Im Allgemeinen sollte jedoch nur ein minimaler Bereich von meist nicht gebundenen Seiten angegeben werden, da das Committen einer großen Anzahl von Seiten, die bereits committet wurden, dazu führen kann, dass der VirtualAlloc2-Aufruf viel länger dauert.

Sie können VirtualAlloc2 verwenden, um einen Block von Seiten zu reservieren und dann zusätzliche Aufrufe an VirtualAlloc2 zu tätigen, um einzelne Seiten aus dem reservierten Block zu committen. Dadurch kann ein Prozess einen Bereich des virtuellen Adressraums reservieren, ohne physischen Speicher zu verwenden, bis er benötigt wird.

Wenn der lpAddress-Parameter nicht NULL ist, verwendet die Funktion die Parameter lpAddress und dwSize , um den Bereich der zuzuordnenden Seiten zu berechnen. Der aktuelle Zustand des gesamten Seitenbereichs muss mit dem Vom flAllocationType-Parameter angegebenen Zuordnungstyp kompatibel sein. Andernfalls schlägt die Funktion fehl, und keine der Seiten wird zugeordnet. Diese Kompatibilitätsanforderung schließt das Commit einer bereits zugesicherten Seite nicht aus. Sehen Sie sich die vorherige Liste an.

Um dynamisch generierten Code auszuführen, verwenden Sie VirtualAlloc2 , um Arbeitsspeicher zuzuweisen, und die VirtualProtectEx-Funktion , um PAGE_EXECUTE Zugriff zu gewähren.

Die VirtualAlloc2-Funktion kann verwendet werden, um einen AWE-Bereich ( Address Windowing Extensions ) im virtuellen Adressraum eines angegebenen Prozesses zu reservieren. Dieser Bereich des Arbeitsspeichers kann dann verwendet werden, um physische Seiten nach Bedarf in und aus dem virtuellen Arbeitsspeicher zuzuordnen. Die werte MEM_PHYSICAL und MEM_RESERVE müssen im AllocationType-Parameter festgelegt werden. Der MEM_COMMIT-Wert darf nicht festgelegt werden. Der Seitenschutz muss auf PAGE_READWRITE festgelegt werden.

Die VirtualFreeEx-Funktion kann eine committete Seite aufheben, den Speicher der Seite freigeben oder gleichzeitig die Aussetzung aufheben und eine committe Seite freigeben. Es kann auch eine reservierte Seite freigeben, sodass sie eine kostenlose Seite ist.

Beim Erstellen einer region, die ausführbar ist, trägt das aufrufende Programm die Verantwortung für die Sicherstellung der Cachekohärenz über einen entsprechenden Aufruf von FlushInstructionCache , sobald der Code festgelegt wurde. Andernfalls können Versuche, Code aus der neu ausführbaren Region auszuführen, zu unvorhersehbaren Ergebnissen führen.

Beispiele

Szenario 1: Erstellen Sie einen kreisförmigen Puffer, indem Sie zwei benachbarte Ansichten desselben freigegebenen Speicherabschnitts zuordnen.

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

Szenario 2: Geben Sie bei der Zuweisung von Arbeitsspeicher einen bevorzugten NUMA-Knoten an.


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

Szenario 3: Ordnen Sie Arbeitsspeicher in einem bestimmten virtuellen Adressbereich (in diesem Beispiel unter 4 GB) und mit einer bestimmten Ausrichtung zu.


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

Anforderungen

   
Unterstützte Mindestversion (Client) Windows 10 [nur Desktop-Apps]
Unterstützte Mindestversion (Server) Windows Server 2016 [nur Desktop-Apps]
Zielplattform Windows
Kopfzeile memoryapi.h (einschließlich Windows.h)
Bibliothek onecore.lib
DLL Kernel32.dll

Siehe auch

Speicherverwaltungsfunktionen

ReadProcessMemory

Funktionen des virtuellen Speichers

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

Virtualquery

WriteProcessMemory