VirtualAlloc2-Funktion (memoryapi.h)

Reserviert, commits oder ändert den Zustand eines Speicherbereichs innerhalb des virtuellen Adressraums eines angegebenen Prozesses (zugewiesener Speicher 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 weist Speicher innerhalb des virtuellen Adressraums dieses Prozesses zu.

Das Handle muss über das PROCESS_VM_OPERATION Zugriffsrecht verfügen. Weitere Informationen finden Sie unter Prozesssicherheits- und Zugriffsberechtigungen.

Wenn ProcessNULL-ist, weist die Funktion Speicher für den Aufrufvorgang zu.

[in, optional] BaseAddress

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

Wenn BaseAddress-NULL-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 Systemzuordnungs-Granularität sein. Um die Zuordnungs granularität zu bestimmen, verwenden Sie die GetSystemInfo--Funktion.

Wenn sich diese Adresse in einer Enklave befindet, die Sie nicht initialisiert haben, indem Sie InitializeEnklaveaufrufen, VirtualAlloc2 eine Seite mit Nullen für die Enklave an dieser Adresse zuweist. Die Seite muss zuvor nicht ausgelassen werden und wird nicht mit der EEXTEND-Anweisung des Intel Software Guard Extensions-Programmiermodells gemessen.

Wenn die Adresse in einer Enklave, die Sie initialisiert haben, fehlschlägt der Zuordnungsvorgang mit dem ERROR_INVALID_ADDRESS Fehler. Das gilt für Enklaven, die keine dynamische Speicherverwaltung unterstützen (d. h. SGX1). SGX2-Enklaven erlauben die Zuordnung, und die Seite muss von der Enklave akzeptiert werden, nachdem sie zugeteilt wurde.

[in] Size

Die Größe des Speicherbereichs, der in Byte zugeordnet werden soll.

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

Wenn BaseAddress- nicht NULL-ist, weist die Funktion alle Seiten zu, die einen oder mehrere Bytes im Bereich von BaseAddressBaseAddress+Sizeenthalten. Dies bedeutet beispielsweise, dass ein 2-Byte-Bereich, der eine Seitengrenze überspannt, die funktion beide Seiten zuweist.

[in] AllocationType

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

Wert Bedeutung
MEM_COMMIT
0x00001000
Weist Speichergebühren (von der Gesamtgröße des Arbeitsspeichers und der Auslagerungsdateien auf dem Datenträger) für die angegebenen reservierten Speicherseiten zu. Die Funktion garantiert außerdem, dass der Inhalt null ist, wenn der Aufrufer zu einem späteren Zeitpunkt auf den Speicher zugreift. Tatsächliche physische Seiten werden nur zugewiesen, wenn/bis tatsächlich auf die virtuellen Adressen zugegriffen wird.

Rufen Sie VirtualAlloc2 mit MEM_COMMIT | MEM_RESERVEauf, um Seiten in einem Schritt zu reservieren und zu übernehmen.

Wenn Sie versuchen, einen bestimmten Adressbereich zu übernehmen, indem Sie MEM_COMMIT ohne MEM_RESERVE und eine nichtNULL-BaseAddress- angeben, tritt ein Fehler auf, es sei denn, der gesamte Bereich wurde bereits reserviert. Der resultierende Fehlercode ist ERROR_INVALID_ADDRESS.

Ein Versuch, eine Seite zu übernehmen, die bereits zugesichert wurde, führt nicht dazu, dass die Funktion fehlschlägt. Dies bedeutet, dass Sie Seiten übernehmen können, ohne zuerst den aktuellen Verpflichtungsstatus jeder Seite zu bestimmen.

Wenn BaseAddress- eine Adresse innerhalb einer Enklave angibt, muss AllocationType-MEM_COMMITsein.

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

Sie übernehmen reservierte Seiten, indem Sie VirtualAlloc2 erneut mit MEM_COMMITaufrufen. Rufen Sie VirtualAlloc2 mit MEM_COMMIT | MEM_RESERVEauf, um Seiten in einem Schritt zu reservieren und zu übernehmen.

Andere Speicherzuweisungsfunktionen, z. B. malloc und LocalAlloc-, können erst reservierten Speicher verwenden, wenn sie 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, physischen Arbeitsspeicher usw.). Wenn Sie einen Platzhalter ersetzen, müssen BaseAddress- und Size exakt 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, um diese Zuordnung wieder einem Platzhalter zu geben, lesen Sie den dwFreeType Parameter von VirtualFree und VirtualFreeEx.

Ein Platzhalter ist ein Typ des reservierten Speicherbereichs.

MEM_RESERVE_PLACEHOLDER
0x00040000
Rufen Sie zum Erstellen eines Platzhalters VirtualAlloc2- mit MEM_RESERVE | MEM_RESERVE_PLACEHOLDER und PageProtection- auf PAGE_NOACCESSfestgelegt. Informationen zum Freigeben/Teilen/Zusammenlegen 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- angegebenen Speicherbereich und Size nicht mehr von Interesse sind. Die Seiten sollten nicht aus der Auslagerungsdatei gelesen oder in die Auslagerungsdatei geschrieben werden. Der Speicherblock wird jedoch später wieder verwendet, sodass er nicht mehr ausgelassen werden sollte. Dieser Wert kann nicht mit einem anderen Wert verwendet werden.

Die Verwendung dieses Werts garantiert nicht, dass der mit MEM_RESET betriebene Bereich Nullen enthält. Wenn der Bereich Nullen enthalten soll, dekommitieren Sie den Speicher, und übermitteln Sie ihn dann erneut.

Wenn Sie MEM_RESETverwenden, ignoriert die funktion VirtualAlloc2 den Wert von fProtect. Sie müssen jedoch weiterhin fProtect- 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, der durch BaseAddress und Size für den Aufrufer von Interesse ist und versucht, die Auswirkungen von MEM_RESETrückgängig zu machen. Wenn die Funktion erfolgreich ist, bedeutet dies, dass alle Daten im angegebenen Adressbereich intakt sind. Wenn die Funktion fehlschlägt, wurden mindestens einige der Daten im Adressbereich durch Nullen ersetzt.

Dieser Wert kann nicht mit einem anderen Wert verwendet werden. Wenn MEM_RESET_UNDO für einen Adressbereich aufgerufen wird, der zuvor nicht MEM_RESET wurde, ist das Verhalten nicht definiert. Wenn Sie MEM_RESETangeben, ignoriert die VirtualAlloc2--Funktion den Wert PageProtection-. Sie müssen jedoch weiterhin PageProtection- 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 wie angegeben angeben.

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

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

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

MEM_64K_PAGES
0x20400000
Ein Hinweis auf das Betriebssystem, um den Speicher nach Möglichkeit mithilfe von 64K-Seiten zuzuordnen.

Eine 64K-Seite ist eine Speicherregion mit einer Größe von 64 KB, virtuell und physisch zusammenhängend und virtuell und physisch an einer 64K-Grenze ausgerichtet.

Standardmäßig ist der mit MEM_64K_PAGES zugewiesene Arbeitsspeicher seitenfähig, und physische Seiten, die den Speicher sichern, werden bei Bedarf (zum Zeitpunkt des Zugriffs) zugewiesen. Wenn der physische Speicher zu fragmentiert ist, um eine physisch zusammenhängende 64K-Seite zusammenzustellen, kann stattdessen ein Teil oder ein Teil einer MEM_64K_PAGES-Zuordnung mit nicht zusammenhängenden kleinen Seiten zugeordnet werden.

Wenn MEM_64K_PAGES mit dem MEM_EXTENDED_PARAMETER_NONPAGED-Attribut kombiniert wird, wird die Zuordnung mit nicht seitenseitigen 64K-Seiten zugeordnet. Wenn in diesem Fall zusammenhängende 64K-Seiten nicht abgerufen werden können, schlägt die Zuordnung fehl.

Wenn MEM_64K_PAGES angegeben ist, müssen die Parameter "Size" und "BaseAddress" beide Vielfache von 64 KB sein (BaseAddress kann NULL sein).

MEM_PHYSICAL
0x00400000
Reserviert einen Adressbereich, der verwendet werden kann, um Adressfenstererweiterungen (AWE)-Seiten zuzuordnen.

Dieser Wert muss mit MEM_RESERVE und keinen anderen Werten verwendet werden.

MEM_TOP_DOWN
0x00100000
Weist Speicher mit der höchsten möglichen Adresse zu. Dies kann langsamer als die regulären Zuordnungen sein, insbesondere wenn viele Zuordnungen vorhanden sind.

[in] PageProtection

Der Speicherschutz für den Bereich der zuzuordnenden Seiten. Wenn die Seiten zugesichert werden, können Sie eine der Speicherschutzkonstantenangeben.

Wenn BaseAddress- eine Adresse innerhalb einer Enklave angibt, kann PageProtection keine der folgenden Werte sein:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Beim Zuweisen des dynamischen Speichers für eine Enklave muss der PageProtection-parameterPAGE_READWRITE oder PAGE_EXECUTE_READWRITEsein.

[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 von entweder MemExtendedParameterAddressRequirements oder MemExtendedParameterNumaNodehaben. Wenn kein MemExtendedParameterNumaNode erweiterter Parameter bereitgestellt wird, ist das Verhalten identisch mit den Funktionen VirtualAlloc/MapViewOfFile (d. a. der bevorzugte NUMA-Knoten für die physischen Seiten wird basierend auf dem idealen Prozessor des Threads bestimmt, der zuerst auf den Speicher zugreift).

[in] ParameterCount

Die Anzahl der erweiterten Parameter, auf die durch ExtendedParametersverwiesen wird.

Rückgabewert

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

Wenn die Funktion fehlschlägt, ist der Rückgabewert NULL-. Rufen Sie GetLastErrorauf, um erweiterte Fehlerinformationen zu erhalten.

Bemerkungen

Mit dieser Funktion können Sie Folgendes angeben:

  • einen Bereich des virtuellen Adressraums und eine Beschränkung der 2-Ausrichtung für neue Zuordnungen
  • eine beliebige Anzahl erweiterter Parameter
  • ein bevorzugter NUMA-Knoten für den physischen Speicher als erweiterter Parameter (siehe den parameter ExtendedParameters)
  • ein Platzhaltervorgang (insbesondere Ersetzung).

Diese API bietet spezielle Techniken zum Verwalten des virtuellen Speichers zur Unterstützung leistungsstarker Spiele und Serveranwendungen. Platzhalter ermöglichen beispielsweise, dass ein reservierter Speicherbereich explizit partitioniert, überlagert und neu zugeordnet wird; Dies kann verwendet werden, um beliebig erweiterbare Bereiche oder virtuelle Speicherringpuffer zu implementieren. VirtualAlloc2- ermöglicht auch das Zuordnen von Arbeitsspeicher mit einer bestimmten Speicherausrichtung.

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

  • Übernehmen eines Bereichs reservierter Seiten
  • Reservieren einer Region mit kostenlosen Seiten
  • Gleichzeitiges Reservieren und Übernehmen einer Region kostenloser Seiten

VirtualAlloc2- können bereits zugesicherte Seiten übernehmen, jedoch keine bereits reservierten Seiten reservieren. Dies bedeutet, dass Sie einen Seitenbereich übernehmen können, unabhängig davon, ob sie bereits zugesichert wurden, und die Funktion schlägt nicht fehl. Im Allgemeinen sollten jedoch nur ein minimaler Bereich von meist nicht ausgelassenen Seiten angegeben werden, da das Commit einer großen Anzahl von bereits zugesicherten Seiten dazu führen kann, dass der VirtualAlloc 2-Aufruf viel länger dauert.

Sie können VirtualAlloc2- verwenden, um einen Seitenblock zu reservieren und dann zusätzliche Aufrufe an VirtualAlloc2 vorzunehmen, um einzelne Seiten aus dem reservierten Block zu übernehmen. Dadurch kann ein Prozess einen Bereich seines virtuellen Adressraums reservieren, ohne physischen Speicher zu verbrauchen, bis er benötigt wird.

Wenn der parameter lpAddress nicht NULList, verwendet die Funktion die lpAddress und dwSize Parameter, um den bereich der zuzuordnenden Seiten zu berechnen. Der aktuelle Zustand des gesamten Seitenbereichs muss mit dem Typ der Zuordnung kompatibel sein, die vom flAllocationType Parameter angegeben wurde. Andernfalls schlägt die Funktion fehl, und keiner der Seiten wird zugewiesen. Diese Kompatibilitätsanforderung schließt das Commit einer bereits zugesicherten Seite nicht aus; siehe die vorherige Liste.

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 eine Adressfenstererweiterungen-Region (Address Windowing Extensions, AWE) innerhalb des virtuellen Adressraums eines angegebenen Prozesses zu reservieren. Dieser Speicherbereich kann dann verwendet werden, um physische Seiten nach Bedarf der Anwendung in und außerhalb des virtuellen Arbeitsspeichers zuzuordnen. Die werte MEM_PHYSICAL und MEM_RESERVE müssen im parameter AllocationType festgelegt werden. Der wert MEM_COMMIT darf nicht festgelegt werden. Der Seitenschutz muss auf PAGE_READWRITEfestgelegt werden.

Die VirtualFreeEx--Funktion kann eine zugesicherte Seite dekommitieren, den Speicher der Seite freigeben oder gleichzeitig eine zugesicherte Seite aufheben und freigeben. Sie kann auch eine reservierte Seite freigeben, wodurch sie eine kostenlose Seite wird.

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, nachdem der Code eingerichtet wurde. Andernfalls kann der Versuch, Code aus dem neu ausführbaren Bereich auszuführen, zu unvorhersehbaren Ergebnissen führen.

Beispiele

Szenario 1. Erstellen Sie einen Zirkelpuffer, 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 beim Zuordnen des Arbeitsspeichers 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. Weisen Sie Speicher in einem bestimmten virtuellen Adressbereich (unter 4 GB in diesem Beispiel) 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

Anforderung Wert
mindestens unterstützte Client- Windows 10 [nur Desktop-Apps]
mindestens unterstützte Server- Windows Server 2016 [nur Desktop-Apps]
Zielplattform- Fenster
Header- memoryapi.h (include Windows.h)
Library onecore.lib
DLL- Kernel32.dll

Siehe auch

Speicherverwaltungsfunktionen

ReadProcessMemory-

virtuelle Speicherfunktionen

VirtualAllocExNuma

VirtualFreeEx-

VirtualLock-

VirtualProtect-

VirtualQuery-

WriteProcessMemory-