Partager via


VirtualAlloc2, fonction (memoryapi.h)

Réserve, valide ou modifie l’état d’une région de mémoire dans l’espace d’adressage virtuel d’un processus spécifié (la mémoire allouée est initialisée à zéro).

Syntaxe

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

Paramètres

[in, optional] Process

Handle vers un processus. La fonction alloue de la mémoire dans l’espace d’adressage virtuel de ce processus.

Le handle doit disposer du droit d’accès PROCESS_VM_OPERATION. Pour plus d’informations, consultez Processus de sécurité et de droits d’accès.

Si processus est NULL, la fonction alloue de la mémoire pour le processus appelant.

[in, optional] BaseAddress

Pointeur qui spécifie une adresse de départ souhaitée pour la région des pages que vous souhaitez allouer.

Si baseAddress est NULL, la fonction détermine où allouer la région.

Si baseAddress n’est pas NULL, toute structure MEM_ADDRESS_REQUIREMENTS fournie doit être constituée de tous les zéros, et l’adresse de base doit être un multiple de la granularité d’allocation du système. Pour déterminer la granularité d’allocation, utilisez la fonction GetSystemInfo.

Si cette adresse se trouve dans une enclave que vous n’avez pas initialisée en appelant InitializeEnclave, VirtualAlloc2 alloue une page de zéros pour l’enclave à cette adresse. La page doit être précédemment non validée et ne sera pas mesurée avec l’instruction EEXTEND du modèle de programmation Extensions Intel Software Guard.

Si l’adresse dans une enclave que vous avez initialisée, l’opération d’allocation échoue avec l’erreur ERROR_INVALID_ADDRESS. Cela est vrai pour les enclaves qui ne prennent pas en charge la gestion de la mémoire dynamique (par exemple SGX1). Les enclaves SGX2 autorisent l’allocation et la page doit être acceptée par l’enclave une fois qu’elle a été allouée.

[in] Size

Taille de la région de mémoire à allouer, en octets.

La taille doit toujours être un multiple de la taille de page.

Si baseAddress n’est pas NULL, la fonction alloue toutes les pages qui contiennent un ou plusieurs octets de la plage de BaseAddress à +Size. Cela signifie, par exemple, qu’une plage de 2 octets qui chevauche une limite de page entraîne l’allocation des deux pages par fonction.

[in] AllocationType

Type d’allocation de mémoire. Ce paramètre doit contenir l’une des valeurs suivantes.

Valeur Signification
MEM_COMMIT
0x00001000
Alloue des frais de mémoire (à partir de la taille globale de la mémoire et des fichiers de pagination sur le disque) pour les pages de mémoire réservée spécifiées. La fonction garantit également que lorsque l’appelant accède ultérieurement à la mémoire, le contenu sera égal à zéro. Les pages physiques réelles ne sont pas allouées, sauf si/tant que les adresses virtuelles ne sont pas réellement accessibles.

Pour réserver et valider des pages en une seule étape, appelez VirtualAlloc2 avec MEM_COMMIT | MEM_RESERVE.

La tentative de validation d’une plage d’adresses spécifique en spécifiant MEM_COMMIT sans MEM_RESERVE et unNULLBaseAddress échoue, sauf si la plage entière a déjà été réservée. Le code d’erreur résultant est ERROR_INVALID_ADDRESS.

Une tentative de validation d’une page déjà validée n’entraîne pas l’échec de la fonction. Cela signifie que vous pouvez valider des pages sans d’abord déterminer l’état d’engagement actuel de chaque page.

Si baseAddress spécifie une adresse dans une enclave, AllocationType doit être MEM_COMMIT.

MEM_RESERVE
0x00002000
Réserve une plage d’espace d’adressage virtuel du processus sans allouer de stockage physique réel en mémoire ou dans le fichier de pagination sur le disque.

Vous validez les pages réservées en appelant VirtualAlloc2 à nouveau avec MEM_COMMIT. Pour réserver et valider des pages en une seule étape, appelez VirtualAlloc2 avec MEM_COMMIT | MEM_RESERVE.

D’autres fonctions d’allocation de mémoire, telles que malloc et LocalAlloc, ne peuvent pas utiliser la mémoire réservée tant qu’elle n’a pas été libérée.

MEM_REPLACE_PLACEHOLDER
0x00004000
Remplace un espace réservé par une allocation privée normale. Seules les vues de sections de données/pf-backed sont prises en charge (aucune image, mémoire physique, etc.). Lorsque vous remplacez un espace réservé, BaseAddress et Taille doit correspondre exactement à celles de l’espace réservé, et toute structure MEM_ADDRESS_REQUIREMENTS fournie doit se composer de tous les zéros.

Après avoir remplacé un espace réservé par une allocation privée, pour libérer cette allocation vers un espace réservé, consultez le paramètre dwFreeType de VirtualFree et VirtualFreeEx.

Un espace réservé est un type de région de mémoire réservée.

MEM_RESERVE_PLACEHOLDER
0x00040000
Pour créer un espace réservé, appelez VirtualAlloc2 avec MEM_RESERVE | MEM_RESERVE_PLACEHOLDER et PageProtection défini sur PAGE_NOACCESS. Pour libérer/fractionner/fusionner un espace réservé, consultez le paramètre dwFreeType de VirtualFree et VirtualFreeEx.

Un espace réservé est un type de région de mémoire réservée.

MEM_RESET
0x00080000
Indique que les données de la plage de mémoire spécifiées par BaseAddress et Taille n’est plus intéressante. Les pages ne doivent pas être lues ou écrites dans le fichier de pagination. Toutefois, le bloc de mémoire sera utilisé à nouveau ultérieurement, de sorte qu’il ne doit pas être omis. Cette valeur ne peut pas être utilisée avec une autre valeur.

L’utilisation de cette valeur ne garantit pas que la plage utilisée avec MEM_RESET contiendra des zéros. Si vous souhaitez que la plage contienne des zéros, supprimez la mémoire, puis recommencez-la.

Lorsque vous utilisez MEM_RESET, la fonction VirtualAlloc2 ignore la valeur de fProtect. Toutefois, vous devez toujours définir fProtect sur une valeur de protection valide, telle que PAGE_NOACCESS.

VirtualAlloc2 retourne une erreur si vous utilisez MEM_RESET et que la plage de mémoire est mappée à un fichier. Une vue partagée est acceptable uniquement si elle est mappée à un fichier de pagination.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO ne doit être appelé que sur une plage d’adresses à laquelle MEM_RESET a été appliquée précédemment. Il indique que les données de la plage de mémoire spécifiée par BaseAddress et Taille intéressent l’appelant et tentent d’inverser les effets de MEM_RESET. Si la fonction réussit, cela signifie que toutes les données de la plage d’adresses spécifiée sont intactes. Si la fonction échoue, au moins certaines données de la plage d’adresses ont été remplacées par des zéros.

Cette valeur ne peut pas être utilisée avec une autre valeur. Si MEM_RESET_UNDO est appelée sur une plage d’adresses qui n’était pas MEM_RESET précédemment, le comportement n’est pas défini. Lorsque vous spécifiez MEM_RESET, la fonction VirtualAlloc2 ignore la valeur de PageProtection. Toutefois, vous devez toujours définir PageProtection sur une valeur de protection valide, telle que PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 et Windows XP : l’indicateur MEM_RESET_UNDO n’est pas pris en charge tant que Windows 8 et Windows Server 2012.

 

Ce paramètre peut également spécifier les valeurs suivantes, comme indiqué.

Valeur Signification
MEM_LARGE_PAGES
0x20000000
Alloue de la mémoire à l’aide de prise en charge des grandes pages.

La taille et l’alignement doivent être un multiple du minimum de grande page. Pour obtenir cette valeur, utilisez la fonction GetLargePageMinimum.

Si vous spécifiez cette valeur, vous devez également spécifier MEM_RESERVE et MEM_COMMIT.

MEM_64K_PAGES
0x20400000
Indicateur du système d’exploitation pour mapper la mémoire à l’aide de pages 64K, si possible.

Une page 64K est une région de mémoire de taille de 64 Ko, pratiquement et physiquement contiguë, et pratiquement et physiquement alignée sur une limite de 64 Ko.

Par défaut, la mémoire allouée à l’aide de MEM_64K_PAGES est paginable et les pages physiques qui sauvegardent la mémoire sont allouées à la demande (au moment de l’accès). Si la mémoire physique est trop fragmentée pour assembler une page 64K physiquement contiguë, toute ou partie d’une allocation de MEM_64K_PAGES peut être mappée à l’aide de petites pages non contiguës à la place.

Si MEM_64K_PAGES est combinée avec l’attribut MEM_EXTENDED_PARAMETER_NONPAGED, l’allocation est mappée à l’aide de pages non paginées 64K. Dans ce cas, si les pages contiguës 64K ne peuvent pas être obtenues, l’allocation échoue.

Si MEM_64K_PAGES est spécifié, les paramètres Size et BaseAddress doivent tous deux être multiples de 64 Ko (BaseAddress peut être NULL).

MEM_PHYSICAL
0x00400000
Réserve une plage d’adresses qui peut être utilisée pour mapper pages AWE (Address Windowing Extensions).

Cette valeur doit être utilisée avec MEM_RESERVE et aucune autre valeur.

MEM_TOP_DOWN
0x00100000
Alloue de la mémoire à l’adresse la plus élevée possible. Cela peut être plus lent que les allocations régulières, en particulier lorsqu’il existe de nombreuses allocations.

[in] PageProtection

Protection de la mémoire pour la région des pages à allouer. Si les pages sont validées, vous pouvez spécifier l’une des constantes de protection de la mémoire .

Si BaseAddress spécifie une adresse dans une enclave, pageProtection ne peut pas être l’une des valeurs suivantes :

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

Lors de l’allocation de mémoire dynamique pour une enclave, le paramètre pageProtection doit être PAGE_READWRITE ou PAGE_EXECUTE_READWRITE.

[in, out, optional] ExtendedParameters

Pointeur facultatif vers un ou plusieurs paramètres étendus de type MEM_EXTENDED_PARAMETER. Chacune de ces valeurs de paramètre étendues peut elle-même avoir un champ Type de MemExtendedParameterAddressRequirements ou MemExtendedParameterNumaNode. Si aucun paramètre étendu MemExtendedParameterNumaNode est fourni, le comportement est le même que pour les fonctions VirtualAlloc/MapViewOfFile (autrement dit, le nœud NUMA préféré pour les pages physiques est déterminé en fonction du processeur idéal du thread qui accède d’abord à la mémoire).

[in] ParameterCount

Nombre de paramètres étendus pointés par ExtendedParameters.

Valeur de retour

Si la fonction réussit, la valeur de retour est l’adresse de base de la région allouée de pages.

Si la fonction échoue, la valeur de retour est NULL . Pour obtenir des informations d’erreur étendues, appelez GetLastError.

Remarques

Cette fonction vous permet de spécifier :

  • une plage d’espace d’adressage virtuel et une restriction d’alignement power-of-2 pour les nouvelles allocations
  • nombre arbitraire de paramètres étendus
  • un nœud NUMA préféré pour la mémoire physique en tant que paramètre étendu (consultez le paramètre ExtendedParameters)
  • une opération d’espace réservé (en particulier, remplacement).

Cette API fournit des techniques spécialisées de gestion de la mémoire virtuelle pour prendre en charge les jeux hautes performances et les applications serveur. Par exemple, les espaces réservés permettent à une plage de mémoire réservée d’être partitionnée explicitement, superposée et mappée à nouveau ; cela peut être utilisé pour implémenter des régions arbitrairement extensibles ou des mémoires tampons d’anneau de mémoire virtuelle. VirtualAlloc2 permet également d’allouer de la mémoire avec un alignement spécifique de la mémoire.

Chaque page a un état de page associé. La fonction VirtualAlloc2 peut effectuer les opérations suivantes :

  • Valider une région de pages réservées
  • Réserver une région de pages gratuites
  • Réserver et valider simultanément une région de pages libres

VirtualAlloc2 peut valider des pages déjà validées, mais ne peut pas réserver des pages déjà réservées. Cela signifie que vous pouvez valider une plage de pages, qu’elles aient déjà été validées et que la fonction ne échoue pas. En règle générale, seule une plage minimale de pages principalement non validées doit être spécifiée, car la validation d’un grand nombre de pages déjà validées peut entraîner la VirtualAlloc2 appel de prendre beaucoup plus de temps.

Vous pouvez utiliser VirtualAlloc2 pour réserver un bloc de pages, puis effectuer des appels supplémentaires à VirtualAlloc2 pour valider des pages individuelles à partir du bloc réservé. Cela permet à un processus de réserver une plage de son espace d’adressage virtuel sans consommer de stockage physique jusqu’à ce qu’il soit nécessaire.

Si le paramètre lpAddress n’est pas NULL, la fonction utilise les paramètres lpAddress et dwSize pour calculer la région des pages à allouer. L’état actuel de la plage entière de pages doit être compatible avec le type d’allocation spécifié par le paramètre flAllocationType. Sinon, la fonction échoue et aucune des pages n’est allouée. Cette exigence de compatibilité n’empêche pas la validation d’une page déjà validée ; consultez la liste précédente.

Pour exécuter du code généré dynamiquement, utilisez VirtualAlloc2 pour allouer de la mémoire, et la fonction VirtualProtectEx pour accorder l’accès PAGE_EXECUTE.

La fonction VirtualAlloc2 peut être utilisée pour réserver une zone de mémoire AWE (Address Windowing Extensions) dans l’espace d’adressage virtuel d’un processus spécifié. Cette région de mémoire peut ensuite être utilisée pour mapper des pages physiques dans et hors mémoire virtuelle, selon les besoins de l’application. Les valeurs MEM_PHYSICAL et MEM_RESERVE doivent être définies dans le paramètre AllocationType . La valeur MEM_COMMIT ne doit pas être définie. La protection de page doit être définie sur PAGE_READWRITE.

La fonction VirtualFreeEx peut décommit une page validée, libérer le stockage de la page ou libérer simultanément une page validée. Il peut également libérer une page réservée, ce qui en fait une page gratuite.

Lors de la création d’une région qui sera exécutable, le programme appelant a la responsabilité de garantir la cohérence du cache via un appel approprié à FlushInstructionCache une fois le code en place. Sinon, les tentatives d’exécution de code hors de la région nouvellement exécutable peuvent produire des résultats imprévisibles.

Exemples

Scénario 1. Créez une mémoire tampon circulaire en mappant deux vues adjacentes de la même section de mémoire partagée.

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

Scénario 2. Spécifiez un nœud NUMA préféré lors de l’allocation de la mémoire.


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

Scénario 3. Allouez de la mémoire dans une plage d’adresses virtuelle spécifique (sous 4 Go, dans cet exemple) et avec un alignement spécifique.


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

Exigences

Exigence Valeur
client minimum pris en charge Windows 10 [applications de bureau uniquement]
serveur minimum pris en charge Windows Server 2016 [applications de bureau uniquement]
plateforme cible Windows
d’en-tête memoryapi.h (include Windows.h)
bibliothèque onecore.lib
DLL Kernel32.dll

Voir aussi

fonctions de gestion de la mémoire

ReadProcessMemory

fonctions de mémoire virtuelle

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

virtualQuery

writeProcessMemory