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 訪問許可權。 如需詳細資訊,請參閱 處理安全性和存取權限。
如果
[in, optional] BaseAddress
指標,指定您要配置之頁面區域的所需起始位址。
如果 BaseAddressNULL,則函式會決定配置區域的位置。
如果 BaseAddress 未 NULL,則任何提供的 MEM_ADDRESS_REQUIREMENTS 結構都必須包含所有零,而基地址必須是系統配置粒度的倍數。 若要判斷配置粒度,請使用 GetSystemInfo 函式。
如果此位址位於您尚未呼叫 InitializeEnclave
如果您初始化的記憶體保護區內的位址,則配置作業會失敗,並出現 ERROR_INVALID_ADDRESS 錯誤。 對於不支援易失記憶體管理的記憶體保護區而言,這是事實(亦即 SGX1)。 SGX2 記憶體保護區將允許配置,而且頁面必須在配置後由記憶體保護區接受。
[in] Size
要配置的記憶體區域大小,以位元組為單位。
大小一律必須是頁面大小的倍數。
如果 BaseAddress 不是 NULL,則函式會將範圍中包含一或多個字節的所有頁面配置 BaseAddress 到 BaseAddress+Size。 例如,這表示跨越頁面界限的 2 位元組範圍會導致函式配置這兩個頁面。
[in] AllocationType
記憶體配置的類型。 此參數必須包含下列其中一個值。
價值 | 意義 |
---|---|
|
為指定的保留記憶體分頁配置記憶體費用(從記憶體的整體大小和磁碟上的分頁檔案。 函式也保證當呼叫端稍後最初存取記憶體時,內容會是零。 除非實際存取虛擬位址,否則不會配置實際的實體頁面。
若要在一個步驟中保留和認可頁面,請使用 除非已保留整個範圍,否則嘗試藉由指定不含 MEM_RESERVE 的 MEM_COMMIT,以及非NULLBaseAddress 失敗,以認可特定位址範圍。 產生的錯誤碼 ERROR_INVALID_ADDRESS。 嘗試認可已經認可的頁面,並不會讓函式失敗。 這表示您可以認可頁面,而不需要先判斷每個頁面的目前承諾狀態。 如果 BaseAddress 指定記憶體保護區內的位址,則必須 MEM_COMMITAllocationType 。 |
|
保留進程虛擬位址空間的範圍,而不需在記憶體或磁碟上的分頁檔案中配置任何實際的實體記憶體。
您可以使用 MEM_COMMIT再次呼叫 VirtualAlloc2 來認可保留頁面。 若要在一個步驟中保留和認可頁面,請使用 其他記憶體配置函式,例如 malloc 和 LocalAlloc,在釋放之前,無法使用保留的記憶體。 |
|
以一般私用配置取代佔位元。 僅支持數據/pf 支援的區段檢視(沒有影像、物理記憶體等)。 當您取代佔位元元時,BaseAddress 和 Size 必須完全符合佔位元元的佔位元,而且任何提供的 MEM_ADDRESS_REQUIREMENTS 結構都必須包含所有零。
將佔位元取代為私人配置之後,若要將配置釋放回佔位符, 佔位元是保留的記憶體區域類型。 |
|
若要建立佔位符,請使用 MEM_RESERVE | MEM_RESERVE_PLACEHOLDER 和 PageProtection 設定為 PAGE_NOACCESS呼叫 virtualAlloc2。 若要釋放/分割/聯合佔位符,請參閱 virtualFree 佔位元是保留的記憶體區域類型。 |
|
表示 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 的位址範圍上呼叫 MEM_RESET_UNDO,則行為未定義。 當您指定 MEM_RESET時,VirtualAlloc2 函式會忽略 pageProtection 的值。 不過,您必須將 PageProtection 設定為有效的保護值,例如 PAGE_NOACCESS。 Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP:Windows 8 和 Windows Server 2012 之前不支援 MEM_RESET_UNDO 旗標。 |
此參數也可以指定下列值,如所示。
價值 | 意義 |
---|---|
|
使用大型頁面支援 設定記憶體,。
大小和對齊必須是大頁面最小值的倍數。 若要取得此值,請使用 getLargePageMinimum 函式 如果您指定這個值,您也必須指定 MEM_RESERVE 和 MEM_COMMIT。 |
|
如果可能的話,操作系統會使用 64K 頁對應記憶體的提示。
64K 分頁是記憶體區域,大小為 64K,幾乎和實際連續,而且幾乎和實際對齊在 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類型之一或多個擴充參數的選擇性指標。 每個擴充參數值本身都可以有 MemExtendedParameterAddressRequirements 或 MemExtendedParameterNumaNodeType 字段。 如果未提供 MemExtendedParameterNumaNode 擴充參數,則行為會與 VirtualAlloc/MapViewOfFile 函式相同(亦即,實體頁面的慣用 NUMA 節點是根據第一次存取記憶體之線程的理想處理器來決定)。
[in] ParameterCount
ExtendedParameters 所指向的擴充參數數目。
傳回值
如果函式成功,傳回值就是頁面所配置區域的基位址。
如果函式失敗,則傳回值 NULL。 若要取得擴充的錯誤資訊,請呼叫 GetLastError。
言論
此函式可讓您指定:
- 新配置的虛擬位址空間範圍和 2 的對齊能力限制
- 任意數目的擴充參數
- 作為擴充參數的實體記憶體慣用 NUMA 節點(請參閱 ExtendedParameters 參數)
- 佔位元作業(特別是取代)。
此 API 提供特殊技術來管理虛擬記憶體,以支援高效能遊戲和伺服器應用程式。 例如,佔位元允許明確分割、覆寫和重新對應的保留記憶體範圍;這可用來實作任意擴充區域或虛擬記憶體通道緩衝區。 VirtualAlloc2 也允許配置具有特定記憶體對齊方式的記憶體。
每個頁面都有相關聯的 頁面狀態。 virtualAlloc2 函式
- 認可保留頁面的區域
- 保留免費頁面的區域
- 同時保留並認可免費頁面的區域
VirtualAlloc2 可以認可已認可的頁面,但無法保留已保留的頁面。 這表示不論頁面是否已認可,都可以認可一系列頁面,而且函式不會失敗。 不過,一般而言,應該只指定最少的未認可頁面範圍,因為認可已認可的大量頁面可能會導致 VirtualAlloc2 呼叫花費更長的時間。
您可以使用
如果 lpAddress 參數未 NULL,函式會使用 lpAddress,dwSize 參數來計算要配置的頁面區域。 整個頁面範圍的目前狀態必須與 flAllocationType 參數所指定的配置類型相容。 否則,函式會失敗,且未配置任何頁面。 此相容性需求不會排除認可已認可的頁面;請參閱上述清單。
若要執行動態產生的程式代碼,請使用 VirtualAlloc2 來配置記憶體,並使用 VirtualProtectEx 函式來授與 PAGE_EXECUTE 存取權。
VirtualAlloc2 函式可用來在指定行程的虛擬位址空間內保留記憶體 位址視窗延伸模組 (AWE) 區域。 然後,您可以使用此記憶體區域,將實體頁面對應到應用程式所需的虛擬記憶體和移出。 AllocationType 參數中必須設定 MEM_PHYSICAL 和 MEM_RESERVE 值。 不得設定 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. 在特定的虛擬位址範圍中配置記憶體(在此範例中為低於 4GB),並使用特定對齊方式。
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 [僅限傳統型應用程式] |
目標平臺 | 窗戶 |
標頭 | memoryapi.h (包括 Windows.h) |
連結庫 | onecore.lib |
DLL | Kernel32.dll |