据我了解,要在linux中保留一大块虚拟内存,请使用mmap
和MAP_ANONYMOUS
调用MAP_PRIVATE
,并在Windows上调用等效系统调用{{1} }。
但是,linux提供了VirtualAlloc
来调整内存映射的大小,手册页说明了
更改虚拟地址和内存页面之间的映射
我无法找到Windows的等效系统调用。似乎要重新分配内存,必须使用mremap
而不是HeapAlloc
,然后使用VirtualAlloc
。关于HeapReAlloc
,msdn说
保留内存内容的过程涉及内存复制操作,这可能非常耗时。
那么在Windows中无法重新映射虚拟内存吗?如果没有,为什么不呢?
答案 0 :(得分:3)
Windows上虚拟内存的细粒度控制可以通过Win32 API中的AWE系列函数实现。
初始分配是使用OpenSSL Wiki完成的,正如其名称所示,为您分配实际的物理页面。然后,您可以使用AllocateUserPhysicalPages将这些物理页面映射到虚拟页面,并将其映射到先前使用VirtualAlloc
保留的虚拟地址空间范围。请注意,您可以将已映射的物理页面重新映射到其他虚拟页面。
当您同时处理物理和虚拟内存时,这会带来一组不同的语义。在可能值得一提的一些缺点中,您需要确保存在 no aliasing ;并且您实际上被限制为使用本机页面大小,即您将无法使用大页面。
答案 1 :(得分:1)
这是使用地址窗口扩展(AWE)的有效解决方案。 这个想法是将开始的物理页面临时映射到虚拟内存的末尾。为此,您必须虚拟分配循环数组的两倍大小。
它不像Linux mremap那样方便,但是可以。 关于MSDN文档(https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc2),您还可以在VirtualAlloc2中使用“占位符”,但仅在Windows 10及更高版本上可用。
此解决方案基于MSDN示例(https://msdn.microsoft.com/en-us/library/windows/desktop/aa366531(v=vs.85).aspx)。在运行帐户之前,请记住要获得Windows中帐户的“内存中锁定页”特权。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#define MEMORY_REQUESTED 1024*1024 // request a megabyte
BOOL
LoggedSetLockPagesPrivilege ( HANDLE hProcess,
BOOL bEnable);
BOOL
CyclicMapUserPhysicalPages( void* VirtualAddress,
void* VirtualHeadAddress,
ULONG_PTR NumberOfPages,
ULONG_PTR* PageArray,
DWORD dwPageSize);
void _cdecl main()
{
BOOL bResult; // generic Boolean value
ULONG_PTR NumberOfPages; // number of pages to request
ULONG_PTR NumberOfPagesInitial; // initial number of pages requested
ULONG_PTR *aPFNs; // page info; holds opaque data
PVOID lpMemReserved; // AWE window
SYSTEM_INFO sSysInfo; // useful system information
int PFNArraySize; // memory to request for PFN array
GetSystemInfo(&sSysInfo); // fill the system information structure
_tprintf(_T("This computer has page size %d.\n"), sSysInfo.dwPageSize);
// Calculate the number of pages of memory to request.
NumberOfPages = MEMORY_REQUESTED/sSysInfo.dwPageSize;
_tprintf (_T("Requesting %d pages of memory.\n"), NumberOfPages);
// Calculate the size of the user PFN array.
PFNArraySize = NumberOfPages * sizeof (ULONG_PTR);
_tprintf (_T("Requesting a PFN array of %d bytes.\n"), PFNArraySize);
aPFNs = (ULONG_PTR *) HeapAlloc(GetProcessHeap(), 0, PFNArraySize);
if (aPFNs == NULL)
{
_tprintf (_T("Failed to allocate on heap.\n"));
return;
}
// Enable the privilege.
if( ! LoggedSetLockPagesPrivilege( GetCurrentProcess(), TRUE ) )
{
return;
}
// Allocate the physical memory.
NumberOfPagesInitial = NumberOfPages;
bResult = AllocateUserPhysicalPages( GetCurrentProcess(),
&NumberOfPages,
aPFNs );
if( bResult != TRUE )
{
_tprintf(_T("Cannot allocate physical pages (%u)\n"), GetLastError() );
return;
}
if( NumberOfPagesInitial != NumberOfPages )
{
_tprintf(_T("Allocated only %p pages.\n"), NumberOfPages );
return;
}
// Reserve the virtual memory.
lpMemReserved = VirtualAlloc( NULL,
MEMORY_REQUESTED*2, // NB: Twice the size
MEM_RESERVE | MEM_PHYSICAL,
PAGE_READWRITE );
if( lpMemReserved == NULL )
{
_tprintf(_T("Cannot reserve memory.\n"));
return;
}
// Cyclic Map the physical memory into the window.
void* Head = ((char*)lpMemReserved) + MEMORY_REQUESTED - 6; // Arbitrary Head Address (must be between >= lpMemReserved and <lpMemReserved+MEMORY_REQUESTED)
bResult = CyclicMapUserPhysicalPages( lpMemReserved,
Head,
NumberOfPages,
aPFNs,
sSysInfo.dwPageSize );
if( bResult != TRUE )
{
_tprintf(_T("CyclicMapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
sprintf((char*)Head, "Hello World");
/// unmap Cyclic
bResult = CyclicMapUserPhysicalPages( lpMemReserved,
Head,
NumberOfPages,
NULL,
sSysInfo.dwPageSize );
if( bResult != TRUE )
{
_tprintf(_T("CyclicMapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
// Map the physical memory into the window.
bResult = MapUserPhysicalPages( lpMemReserved,
NumberOfPages,
aPFNs );
if( bResult != TRUE )
{
_tprintf(_T("MapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
if (strcmp((char const*)lpMemReserved, "World"))
{
_tprintf(_T("Mem Content Check failed\n") );
return;
}
// unmap
bResult = MapUserPhysicalPages( lpMemReserved,
NumberOfPages,
NULL );
if( bResult != TRUE )
{
_tprintf(_T("MapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
// Free the physical pages.
bResult = FreeUserPhysicalPages( GetCurrentProcess(),
&NumberOfPages,
aPFNs );
if( bResult != TRUE )
{
_tprintf(_T("Cannot free physical pages, error %u.\n"), GetLastError());
return;
}
// Free virtual memory.
bResult = VirtualFree( lpMemReserved,
0,
MEM_RELEASE );
// Release the aPFNs array.
bResult = HeapFree(GetProcessHeap(), 0, aPFNs);
if( bResult != TRUE )
{
_tprintf(_T("Call to HeapFree has failed (%u)\n"), GetLastError() );
}
}
/*****************************************************************
LoggedSetLockPagesPrivilege: a function to obtain or
release the privilege of locking physical pages.
Inputs:
HANDLE hProcess: Handle for the process for which the
privilege is needed
BOOL bEnable: Enable (TRUE) or disable?
Return value: TRUE indicates success, FALSE failure.
*****************************************************************/
BOOL
LoggedSetLockPagesPrivilege ( HANDLE hProcess,
BOOL bEnable)
{
struct {
DWORD Count;
LUID_AND_ATTRIBUTES Privilege [1];
} Info;
HANDLE Token;
BOOL Result;
// Open the token.
Result = OpenProcessToken ( hProcess,
TOKEN_ADJUST_PRIVILEGES,
& Token);
if( Result != TRUE )
{
_tprintf( _T("Cannot open process token.\n") );
return FALSE;
}
// Enable or disable?
Info.Count = 1;
if( bEnable )
{
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
Info.Privilege[0].Attributes = 0;
}
// Get the LUID.
Result = LookupPrivilegeValue ( NULL,
SE_LOCK_MEMORY_NAME,
&(Info.Privilege[0].Luid));
if( Result != TRUE )
{
_tprintf( _T("Cannot get privilege for %s.\n"), SE_LOCK_MEMORY_NAME );
return FALSE;
}
// Adjust the privilege.
Result = AdjustTokenPrivileges ( Token, FALSE,
(PTOKEN_PRIVILEGES) &Info,
0, NULL, NULL);
// Check the result.
if( Result != TRUE )
{
_tprintf (_T("Cannot adjust token privileges (%u)\n"), GetLastError() );
return FALSE;
}
else
{
if( GetLastError() != ERROR_SUCCESS )
{
_tprintf (_T("Cannot enable the SE_LOCK_MEMORY_NAME privilege; "));
_tprintf (_T("please check the local policy.\n"));
return FALSE;
}
}
CloseHandle( Token );
return TRUE;
}
/*
--->(tail) (head) ----- ~~~~>
v v
+-------------------------------|-------------------------------+
| virtual memory |
+-------------------------------|-------------------------------+
<--- Memory Requested Size ---->
*/
BOOL CyclicMapUserPhysicalPages(void* VirtualAddress, void* VirtualHeadAddress, ULONG_PTR NumberOfPages, ULONG_PTR* PageArray, DWORD dwPageSize){
ULONG_PTR iStartPage = (ULONG_PTR(VirtualHeadAddress)-ULONG_PTR(VirtualAddress))/dwPageSize;
void* pEnd = ((BYTE*)VirtualAddress)+dwPageSize*iStartPage;
void* pStart = ((BYTE*)VirtualAddress)+dwPageSize*NumberOfPages;
BOOL bResult = MapUserPhysicalPages( pEnd, NumberOfPages-iStartPage, PageArray ? (PageArray+iStartPage) : NULL );
if( !bResult )
return FALSE;
if (iStartPage)
{
bResult = MapUserPhysicalPages( pStart, iStartPage, PageArray );
if( !bResult ){
if (PageArray)
MapUserPhysicalPages( pEnd, NumberOfPages-iStartPage, NULL );
return FALSE;
}
}
return TRUE;
}
答案 2 :(得分:-1)
您可以使用Address Windowing Extensions(AWE)执行此操作。例如,您可以保留两个虚拟内存区域,然后将它们一个接一个地映射到同一个物理区域。或者您可以保留单个虚拟区域,但映射它的不同部分。
它不像Linux mremap那样方便,但它可以工作。
请查看地址窗口扩展(AWE): https://msdn.microsoft.com/en-us/library/windows/desktop/aa366531(v=vs.85).aspx
我的工作代码,供您参考,基于MSDN示例。请记住在内存中获取&#34;锁定页面&#34;在运行之前在Windows中为您的帐户授予特权。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#define MEMORY_REQUESTED 1024*1024 // request a megabyte
BOOL
LoggedSetLockPagesPrivilege(HANDLE hProcess,
BOOL bEnable);
void _cdecl main()
{
BOOL bResult; // generic Boolean value
ULONG_PTR NumberOfPages; // number of pages to request
ULONG_PTR NumberOfPagesInitial; // initial number of pages requested
ULONG_PTR *aPFNs; // page info; holds opaque data
PVOID lpMemReserved; // AWE window
PVOID lpMemReserved2; // AWE window
SYSTEM_INFO sSysInfo; // useful system information
int PFNArraySize; // memory to request for PFN array
GetSystemInfo(&sSysInfo); // fill the system information structure
_tprintf(_T("This computer has page size %d.\n"), sSysInfo.dwPageSize);
// Calculate the number of pages of memory to request.
NumberOfPages = MEMORY_REQUESTED / sSysInfo.dwPageSize;
_tprintf(_T("Requesting %d pages of memory.\n"), NumberOfPages);
// Calculate the size of the user PFN array.
PFNArraySize = NumberOfPages * sizeof(ULONG_PTR);
_tprintf(_T("Requesting a PFN array of %d bytes.\n"), PFNArraySize);
aPFNs = (ULONG_PTR *)HeapAlloc(GetProcessHeap(), 0, PFNArraySize);
if (aPFNs == NULL)
{
_tprintf(_T("Failed to allocate on heap.\n"));
return;
}
// Enable the privilege.
if (!LoggedSetLockPagesPrivilege(GetCurrentProcess(), TRUE))
{
return;
}
// Allocate the physical memory.
NumberOfPagesInitial = NumberOfPages;
bResult = AllocateUserPhysicalPages(GetCurrentProcess(),
&NumberOfPages,
aPFNs);
if (bResult != TRUE)
{
_tprintf(_T("Cannot allocate physical pages (%u)\n"), GetLastError());
return;
}
if (NumberOfPagesInitial != NumberOfPages)
{
_tprintf(_T("Allocated only %p pages.\n"), (void*)NumberOfPages);
return;
}
// Reserve the virtual memory.
lpMemReserved = VirtualAlloc(NULL,
MEMORY_REQUESTED,
MEM_RESERVE | MEM_PHYSICAL,
PAGE_READWRITE);
if (lpMemReserved == NULL)
{
_tprintf(_T("Cannot reserve memory.\n"));
return;
}
lpMemReserved2 = VirtualAlloc(NULL,
MEMORY_REQUESTED,
MEM_RESERVE | MEM_PHYSICAL,
PAGE_READWRITE);
if (lpMemReserved2 == NULL)
{
_tprintf(_T("Cannot reserve memory.\n"));
return;
}
// Map the physical memory into the window.
bResult = MapUserPhysicalPages(lpMemReserved,
NumberOfPages,
aPFNs);
if (bResult != TRUE)
{
_tprintf(_T("MapUserPhysicalPages failed (%u)\n"), GetLastError());
return;
}
else {
int* pa = (int*)lpMemReserved;
pa[1] = pa[100] = 0xF0F0;
_tprintf(_T("MapUserPhysicalPages successfully at %p\n"), lpMemReserved);
}
// unmap
bResult = MapUserPhysicalPages(lpMemReserved,
NumberOfPages,
NULL);
if (bResult != TRUE)
{
_tprintf(_T("MapUserPhysicalPages failed (%u)\n"), GetLastError());
return;
}
//remap
bResult = MapUserPhysicalPages(lpMemReserved2,
NumberOfPages,
aPFNs);
if (bResult != TRUE)
{
_tprintf(_T("Re-MapUserPhysicalPages failed (%u)\n"), GetLastError());
return;
}
else {
int* pa = (int*)lpMemReserved2;
if(pa[1] != pa[100] || pa[100] != 0xF0F0)
_tprintf(_T("Re-MapUserPhysicalPages failed (%u)\n"), GetLastError());
_tprintf(_T("Re-MapUserPhysicalPages successfully at %p\n"), lpMemReserved2);
}
// Free the physical pages.
bResult = FreeUserPhysicalPages(GetCurrentProcess(),
&NumberOfPages,
aPFNs);
if (bResult != TRUE)
{
_tprintf(_T("Cannot free physical pages, error %u.\n"), GetLastError());
return;
}
// Free virtual memory.
bResult = VirtualFree(lpMemReserved,
0,
MEM_RELEASE);
// Release the aPFNs array.
bResult = HeapFree(GetProcessHeap(), 0, aPFNs);
if (bResult != TRUE)
{
_tprintf(_T("Call to HeapFree has failed (%u)\n"), GetLastError());
}
_tprintf(_T("Successfully finished\n"));
}
/*****************************************************************
LoggedSetLockPagesPrivilege: a function to obtain or
release the privilege of locking physical pages.
Inputs:
HANDLE hProcess: Handle for the process for which the
privilege is needed
BOOL bEnable: Enable (TRUE) or disable?
Return value: TRUE indicates success, FALSE failure.
*****************************************************************/
BOOL
LoggedSetLockPagesPrivilege(HANDLE hProcess,
BOOL bEnable)
{
struct {
DWORD Count;
LUID_AND_ATTRIBUTES Privilege[1];
} Info;
HANDLE Token;
BOOL Result;
// Open the token.
Result = OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES,
&Token);
if (Result != TRUE)
{
_tprintf(_T("Cannot open process token.\n"));
return FALSE;
}
// Enable or disable?
Info.Count = 1;
if (bEnable)
{
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
Info.Privilege[0].Attributes = 0;
}
// Get the LUID.
Result = LookupPrivilegeValue(NULL,
SE_LOCK_MEMORY_NAME,
&(Info.Privilege[0].Luid));
if (Result != TRUE)
{
_tprintf(_T("Cannot get privilege for %s.\n"), SE_LOCK_MEMORY_NAME);
return FALSE;
}
// Adjust the privilege.
Result = AdjustTokenPrivileges(Token, FALSE,
(PTOKEN_PRIVILEGES)&Info,
0, NULL, NULL);
// Check the result.
if (Result != TRUE)
{
_tprintf(_T("Cannot adjust token privileges (%u)\n"), GetLastError());
return FALSE;
}
else
{
if (GetLastError() != ERROR_SUCCESS)
{
_tprintf(_T("Cannot enable the SE_LOCK_MEMORY_NAME privilege; "));
_tprintf(_T("please check the local policy.\n"));
return FALSE;
}
}
CloseHandle(Token);
return TRUE;
}