Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Резервирует, фиксирует или изменяет состояние области памяти в виртуальном адресном пространстве указанного процесса (выделенная память инициализирована до нуля).
Синтаксис
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 . Дополнительные сведения см. в разделе "Безопасность процесса" и "Права доступа".
Если процесс имеет значение 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
Тип выделения памяти. Этот параметр должен содержать одно из следующих значений.
Ценность | Значение |
---|---|
|
Выделяет расходы на память (от общего размера памяти и файлов разбиения по страницам на диске) для указанных зарезервированных страниц памяти. Функция также гарантирует, что, когда вызывающий объект позже обращается к памяти, содержимое будет равно нулю. Фактические физические страницы не выделяются, если только не будут доступны виртуальные адреса.
Чтобы зарезервировать и зафиксировать страницы на одном шаге, вызовите VirtualAlloc2 с Попытка зафиксировать определенный диапазон адресов путем указания MEM_COMMIT без MEM_RESERVE и ненулевогоbaseAddress завершается ошибкой, если только весь диапазон уже не зарезервирован. Полученный код ошибки ERROR_INVALID_ADDRESS. Попытка зафиксировать страницу, которая уже зафиксирована, не приводит к сбою функции. Это означает, что страницы можно зафиксировать, не определяя текущее состояние обязательств каждой страницы. Если BaseAddress задает адрес в анклавах, значение AllocationType должно быть MEM_COMMIT. |
|
Резервирует диапазон виртуального адресного пространства процесса без выделения фактического физического хранилища в памяти или в файле разбиения на диск.
Зарезервированные страницы фиксируются путем повторного вызова VirtualAlloc2 с MEM_COMMIT. Чтобы зарезервировать и зафиксировать страницы на одном шаге, вызовите VirtualAlloc2 с Другие функции выделения памяти, такие как malloc и LocalAlloc, не могут использовать зарезервированную память, пока она не будет освобождена. |
|
Заменяет заполнитель обычным частным выделением. Поддерживаются только представления разделов с поддержкой данных и pf (изображения, физическая память и т. д.). При замене заполнителя BaseAddress и Size должны точно соответствовать заполнителю, а любая указанная MEM_ADDRESS_REQUIREMENTS структура должна состоять из всех нулей.
После замены заполнителя частным выделением, чтобы освободить это выделение обратно в заполнитель, см. параметр dwFreeTypeVirtualFree и VirtualFreeEx. Заполнитель — это тип зарезервированной области памяти. |
|
Чтобы создать заполнитель, вызовите VirtualAlloc2 с MEM_RESERVE | MEM_RESERVE_PLACEHOLDER параметром PageProtectionзначение PAGE_NOACCESS. Сведения о том, как освободить или разделить или объединить заполнитель, см. в параметре dwFreeType VirtualFree и VirtualFreeEx.
Заполнитель — это тип зарезервированной области памяти. |
|
Указывает, что данные в диапазоне памяти, заданном BaseAddress и Size , больше не являются интересом. Страницы не должны быть считываются или записываются в файл разбиения на страницы. Однако блок памяти будет использоваться снова позже, поэтому его не следует выводить. Это значение нельзя использовать с любым другим значением.
Использование этого значения не гарантирует, что диапазон, работающий с MEM_RESET , будет содержать нули. Если вы хотите, чтобы диапазон содержал нули, удалите память, а затем снова зафиксируете его. При использовании MEM_RESET функция VirtualAlloc2 игнорирует значение fProtect. Однако для параметра fProtect необходимо задать допустимое значение защиты, например PAGE_NOACCESS. VirtualAlloc2 возвращает ошибку, если используется MEM_RESET , а диапазон памяти сопоставляется с файлом. Общее представление допускается только в том случае, если оно сопоставлено с файлом разбиения на страницы. |
|
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. |
Этот параметр также может указать следующие значения, как указано.
Ценность | Значение |
---|---|
|
Выделяет память с помощью поддержки больших страниц.
Размер и выравнивание должны быть несколькими из минимума больших страниц. Чтобы получить это значение, используйте функцию GetLargePageMinimum . Если указать это значение, необходимо также указать MEM_RESERVE и MEM_COMMIT. |
|
Указание операционной системе сопоставить память с помощью 64K-страниц, если это возможно.
Страница 64K — это область памяти размером 64 КБ, виртуальной и физической, а также практически и физически выровненной по границе 64K. По умолчанию объем памяти, выделенный с помощью MEM_64K_PAGES, доступен для страниц, а физические страницы, которые поддерживают память, выделяются по требованию (во время доступа). Если физическая память слишком фрагментирована для сборки физической 64K-страницы, все или часть выделения MEM_64K_PAGES могут быть сопоставлены с использованием несоотложных небольших страниц. Если MEM_64K_PAGES объединяется с атрибутом MEM_EXTENDED_PARAMETER_NONPAGED , выделение будет сопоставлено с помощью страниц, не являющихся страницами 64K. В этом случае, если не удается получить смежные страницы 64K, выделение завершится ошибкой. Если указан MEM_64K_PAGES, параметры Size и BaseAddress должны иметь несколько 64K (BaseAddress может иметь значение NULL). |
|
Резервирует диапазон адресов, который можно использовать для сопоставления страниц расширений окна адресов (AWE).
Это значение должно использоваться с MEM_RESERVE и другими значениями. |
|
Выделяет память по максимально возможному адресу. Это может быть медленнее регулярных выделений, особенно при наличии большого количества выделений. |
[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,
¶m, 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,
¶m, 1);
}
Требования
Требование | Ценность |
---|---|
Минимальный поддерживаемый клиент | Windows 10 [только классические приложения] |
минимальный поддерживаемый сервер | Windows Server 2016 [только классические приложения] |
целевая платформа | Виндоус |
Header | memoryapi.h (включая Windows.h) |
Library | onecore.lib |
DLL | Kernel32.dll |