Функция VirtualAlloc2 (memoryapi.h)

Резервирует, фиксирует или изменяет состояние области памяти в виртуальном адресном пространстве указанного процесса (выделенная память инициализируется до нуля).

Синтаксис

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

Параметры

[in, optional] Process

Дескриптор процесса. Функция выделяет память в виртуальном адресном пространстве этого процесса.

Дескриптор должен иметь право доступа PROCESS_VM_OPERATION . Дополнительные сведения см. в разделе Обработка прав на безопасность и доступ.

Если параметр Process имеет значение NULL, функция выделяет память для вызывающего процесса.

[in, optional] BaseAddress

Указатель, указывающий требуемый начальный адрес для региона страниц, которые требуется выделить.

Если BaseAddress имеет значение NULL, функция определяет, где следует выделить регион.

Если BaseAddress не имеет значение NULL, то любая предоставленная MEM_ADDRESS_REQUIREMENTS структура должна состоять из всех нулей, а базовый адрес должен быть кратным степени детализации распределения системы. Чтобы определить степень детализации выделения, используйте функцию GetSystemInfo .

Если этот адрес находится в анклаве, который вы не инициализировали, вызвав Метод InitializeEnclave, VirtualAlloc2 выделяет страницу с нулями для анклава по этому адресу. Страница должна быть ранее незафиксирована и не будет измеряться с помощью инструкции EEXTEND модели программирования расширений Intel Software Guard.

Если адрес в инициализированном анклаве, операция выделения завершается ошибкой ERROR_INVALID_ADDRESS . Это справедливо для анклавов, которые не поддерживают динамическое управление памятью (например, SGX1). Анклавы SGX2 разрешают выделение, и страница должна быть принята анклавом после ее выделения.

[in] Size

Размер выделенной области памяти в байтах.

Размер всегда должен быть кратным размеру страницы.

Если baseAddress не имеет значение NULL, функция выделяет все страницы, содержащие один или несколько байтов в диапазоне от BaseAddress до BaseAddress+Size. Это означает, например, что 2-байтовый диапазон между границами страницы приводит к тому, что функция выделяет обе страницы.

[in] AllocationType

Тип выделения памяти. Этот параметр должен содержать одно из следующих значений.

Значение Значение
MEM_COMMIT
0x00001000
Выделяет расходы на память (из общего размера памяти и файлов подкачки на диске) для указанных зарезервированных страниц памяти. Функция также гарантирует, что при первоначальном обращении вызывающего объекта к памяти содержимое будет равно нулю. Фактические физические страницы не выделяются, если не будут доступны виртуальные адреса.

Чтобы зарезервировать и зафиксировать страницы за один шаг, вызовите VirtualAlloc2 с MEM_COMMIT | MEM_RESERVEпомощью команды .

Попытка зафиксировать определенный диапазон адресов путем указания MEM_COMMIT без MEM_RESERVE и baseAddress, отличного от NULL, завершается ошибкой, если только весь диапазон уже не зарезервирован. Полученный код ошибки ERROR_INVALID_ADDRESS.

Попытка фиксации страницы, которая уже зафиксирована, не приводит к сбою функции. Это означает, что вы можете фиксировать страницы, не определяя текущее состояние обязательств каждой страницы.

Если BaseAddress указывает адрес в анклаве, значение AllocationType должно быть MEM_COMMIT.

MEM_RESERVE
0x00002000
Резервирует диапазон виртуального адресного пространства процесса, не выделяя фактическое физическое хранилище в памяти или файл подкачки на диске.

Зарезервированные страницы фиксируются путем повторного вызова VirtualAlloc2 с MEM_COMMIT. Чтобы зарезервировать и зафиксировать страницы за один шаг, вызовите VirtualAlloc2 с MEM_COMMIT | MEM_RESERVEпомощью команды .

Другие функции выделения памяти, такие как malloc и LocalAlloc, не могут использовать зарезервированную память, пока она не будет освобождена.

MEM_REPLACE_PLACEHOLDER
0x00004000
Заменяет заполнитель обычным частным выделением. Поддерживаются только представления разделов с поддержкой данных и pf (без изображений, физической памяти и т. д.). При замене заполнителя BaseAddress и Size должны точно соответствовать значениям заполнителя, а любая предоставленная MEM_ADDRESS_REQUIREMENTS структура должна состоять из всех нулей.

После замены заполнителя частным выделением, чтобы освободить это выделение обратно в заполнитель, см. параметр dwFreeTypevirtualFree и VirtualFreeEx.

Заполнитель — это тип зарезервированной области памяти.

MEM_RESERVE_PLACEHOLDER
0x00040000
Чтобы создать заполнитель, вызовите VirtualAlloc2 с MEM_RESERVE | MEM_RESERVE_PLACEHOLDER параметром PageProtectionPAGE_NOACCESS. Сведения о том, как освободить, разделить или объединить заполнитель, см. в параметрах dwFreeTypeдля VirtualFree и VirtualFreeEx.

Заполнитель — это тип зарезервированной области памяти.

MEM_RESET
0x00080000
Указывает, что данные в диапазоне памяти, заданном BaseAddress и Size , больше не являются интересующими. Страницы не должны считываться из файла подкачки или записываться в нее. Тем не менее, блок памяти будет использоваться снова позже, поэтому он не должен быть списан. Это значение нельзя использовать с каким-либо другим значением.

Использование этого значения не гарантирует, что диапазон, в котором используется MEM_RESET , будет содержать нули. Если вы хотите, чтобы диапазон содержал нули, удалите память, а затем снова зафиксируете ее.

При использовании MEM_RESET функция VirtualAlloc2 игнорирует значение fProtect. Однако необходимо по-прежнему задать для параметра fProtect допустимое значение защиты, например PAGE_NOACCESS.

VirtualAlloc2 возвращает ошибку, если используется MEM_RESET и диапазон памяти сопоставлен с файлом. Общее представление допустимо только в том случае, если оно сопоставлено с файлом подкачки.

MEM_RESET_UNDO
0x1000000
MEM_RESET_UNDO следует вызывать только в диапазоне адресов, к которому MEM_RESET успешно применялся ранее. Он указывает, что данные в указанном диапазоне памяти, заданном BaseAddress и Size , являются интересом для вызывающего объекта и пытается обратить вспять последствия MEM_RESET. Если функция выполняется успешно, это означает, что все данные в указанном диапазоне адресов нетронуты. Если функция завершается сбоем, по крайней мере некоторые данные в диапазоне адресов были заменены нулями.

Это значение нельзя использовать с каким-либо другим значением. Если MEM_RESET_UNDO вызывается в диапазоне адресов, который не был MEM_RESET ранее, поведение не определено. При указании MEM_RESET функция VirtualAlloc2 игнорирует значение PageProtection. Однако необходимо по-прежнему задать для PageProtection допустимое значение защиты, например PAGE_NOACCESS.

Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 и Windows XP: Флаг MEM_RESET_UNDO не поддерживается до Windows 8 и Windows Server 2012.

 

Этот параметр также может указывать указанные ниже значения.

Значение Значение
MEM_LARGE_PAGES
0x20000000
Выделяет память с помощью поддержки больших страниц.

Размер и выравнивание должны быть кратными минимальному значению большой страницы. Чтобы получить это значение, используйте функцию GetLargePageMinimum .

Если указать это значение, необходимо также указать MEM_RESERVE и MEM_COMMIT.

MEM_PHYSICAL
0x00400000
Резервирует диапазон адресов, который можно использовать для сопоставления страниц расширений адресных окон (AWE).

Это значение должно использоваться с MEM_RESERVE и никакими другими значениями.

MEM_TOP_DOWN
0x00100000
Выделяет память по максимально возможному адресу. Это может быть медленнее, чем обычное выделение, особенно при наличии большого количества выделений.

[in] PageProtection

Защита памяти для области выделенных страниц. Если страницы фиксируются, можно указать любую из констант защиты памяти.

Если BaseAddress указывает адрес в анклаве, PageProtection не может иметь одно из следующих значений:

  • PAGE_NOACCESS
  • PAGE_GUARD
  • PAGE_NOCACHE
  • PAGE_WRITECOMBINE

При выделении динамической памяти для анклава параметр PageProtection должен быть PAGE_READWRITE или PAGE_EXECUTE_READWRITE.

[in, out, optional] ExtendedParameters

Необязательный указатель на один или несколько расширенных параметров типа MEM_EXTENDED_PARAMETER. Каждое из этих значений расширенных параметров может иметь поле Typeлибо MemExtendedParameterAddressRequirements , либо MemExtendedParameterNumaNode. Если расширенный параметр MemExtendedParameterNumaNode не указан, поведение будет таким же, как и для функций VirtualAlloc/MapViewOfFile (то есть предпочтительный узел NUMA для физических страниц определяется на основе идеального процессора потока, который сначала обращается к памяти).

[in] ParameterCount

Количество расширенных параметров, на которые указывает ExtendedParameters.

Возвращаемое значение

Если функция выполнена успешно, возвращаемое значение является базовым адресом выделенной области страниц.

Если функция завершается сбоем, возвращается значение NULL. Дополнительные сведения об ошибке можно получить, вызвав GetLastError.

Комментарии

Эта функция позволяет указать:

  • диапазон виртуального адресного пространства и ограничение выравнивания по возможности 2 для новых выделений
  • произвольное число расширенных параметров;
  • предпочтительный узел NUMA для физической памяти в качестве расширенного параметра (см. параметр ExtendedParameters ).
  • операция заполнителя (в частности, замена).

Этот API предоставляет специализированные методы управления виртуальной памятью для поддержки высокопроизводительных игр и серверных приложений. Например, заполнители позволяют явно секционировать, накладывать и повторно сопоставлять зарезервированный диапазон памяти. можно использовать для реализации произвольно расширяемых областей или кольцевых буферов виртуальной памяти. VirtualAlloc2 также позволяет выделять память с определенным выравниванием памяти.

Каждая страница имеет связанное состояние страницы. Функция VirtualAlloc2 может выполнять следующие операции:

  • Фиксация области зарезервированных страниц
  • Зарезервировать область бесплатных страниц
  • Одновременное резервирование и фиксация области свободных страниц

VirtualAlloc2 может фиксировать страницы, которые уже зафиксированы, но не может резервировать страницы, которые уже зарезервированы. Это означает, что вы можете зафиксировать диапазон страниц независимо от того, были ли они уже зафиксированы, и функция не завершится ошибкой. Однако в целом следует указывать только минимальный диапазон в основном незафиксированных страниц, так как фиксация большого количества уже зафиксированных страниц может привести к тому, что вызов VirtualAlloc2 займет гораздо больше времени.

С помощью VirtualAlloc2 можно зарезервировать блок страниц, а затем выполнить дополнительные вызовы VirtualAlloc2 для фиксации отдельных страниц из зарезервированного блока. Это позволяет процессу резервировать диапазон своего виртуального адресного пространства без использования физического хранилища до тех пор, пока он не потребуется.

Если параметр lpAddress не равен NULL, функция использует параметры lpAddress и dwSize для вычисления области выделенных страниц. Текущее состояние всего диапазона страниц должно быть совместимо с типом выделения, заданным параметром flAllocationType . В противном случае функция завершается сбоем и ни одна из страниц не выделяется. Это требование совместимости не исключает фиксацию уже зафиксированной страницы; см. предыдущий список.

Чтобы выполнить динамически создаваемый код, используйте VirtualAlloc2 для выделения памяти и функцию VirtualProtectEx , чтобы предоставить PAGE_EXECUTE доступ.

Функцию VirtualAlloc2 можно использовать для резервирования области памяти AWE в виртуальном адресном пространстве указанного процесса. Затем эту область памяти можно использовать для сопоставления физических страниц с виртуальной памятью и из нее в соответствии с требованиями приложения. Значения MEM_PHYSICAL и MEM_RESERVE должны быть заданы в параметре AllocationType . Значение MEM_COMMIT задавать нельзя. Для защиты страницы необходимо задать значение PAGE_READWRITE.

Функция VirtualFreeEx может высвободить зафиксированную страницу, освобождая хранилище страницы, или одновременно высвободить и освободить зафиксированную страницу. Он также может освободить зарезервированную страницу, сделав ее бесплатной.

При создании региона, который будет исполняемым, вызывающая программа несет ответственность за обеспечение когерентности кэша с помощью соответствующего вызова FlushInstructionCache после установки кода. В противном случае попытки выполнить код из новой исполняемой области могут привести к непредсказуемым результатам.

Примеры

Сценарий 1. Создайте циклический буфер путем сопоставления двух смежных представлений одного раздела общей памяти.

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

Сценарий 2. Укажите предпочтительный узел NUMA при выделении памяти.


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

Сценарий 3. Выделите память в определенном диапазоне виртуальных адресов (ниже 4 ГБ в этом примере) и с определенным выравниванием.


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

Требования

   
Минимальная версия клиента Windows 10 [только классические приложения]
Минимальная версия сервера Windows Server 2016 [только классические приложения]
Целевая платформа Windows
Header memoryapi.h (включая Windows.h)
Библиотека onecore.lib
DLL Kernel32.dll

См. также

Функции управления памятью

ReadProcessMemory

Функции виртуальной памяти

VirtualAllocExNuma

VirtualFreeEx

VirtualLock

VirtualProtect

VirtualQuery

WriteProcessMemory