这是使用地址窗口扩展(AWE)的工作解决方案。其思想是将起始物理页暂时映射到虚拟内存的末尾。为了做到这一点,您必须虚拟分配两倍于循环数组大小的空间。
它不像Linux mremap那样方便,但它可以工作。根据MSDN文档(
https://learn.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
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;
ULONG_PTR NumberOfPages;
ULONG_PTR NumberOfPagesInitial;
ULONG_PTR *aPFNs;
PVOID lpMemReserved;
SYSTEM_INFO sSysInfo;
int PFNArraySize;
GetSystemInfo(&sSysInfo);
_tprintf(_T("This computer has page size %d.\n"), sSysInfo.dwPageSize);
NumberOfPages = MEMORY_REQUESTED/sSysInfo.dwPageSize;
_tprintf (_T("Requesting %d pages of memory.\n"), NumberOfPages);
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;
}
if( ! LoggedSetLockPagesPrivilege( GetCurrentProcess(), TRUE ) )
{
return;
}
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;
}
lpMemReserved = VirtualAlloc( NULL,
MEMORY_REQUESTED*2,
MEM_RESERVE | MEM_PHYSICAL,
PAGE_READWRITE );
if( lpMemReserved == NULL )
{
_tprintf(_T("Cannot reserve memory.\n"));
return;
}
void* Head = ((char*)lpMemReserved) + MEMORY_REQUESTED - 6;
bResult = CyclicMapUserPhysicalPages( lpMemReserved,
Head,
NumberOfPages,
aPFNs,
sSysInfo.dwPageSize );
if( bResult != TRUE )
{
_tprintf(_T("CyclicMapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
sprintf((char*)Head, "Hello World");
bResult = CyclicMapUserPhysicalPages( lpMemReserved,
Head,
NumberOfPages,
NULL,
sSysInfo.dwPageSize );
if( bResult != TRUE )
{
_tprintf(_T("CyclicMapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
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;
}
bResult = MapUserPhysicalPages( lpMemReserved,
NumberOfPages,
NULL );
if( bResult != TRUE )
{
_tprintf(_T("MapUserPhysicalPages failed (%u)\n"), GetLastError() );
return;
}
bResult = FreeUserPhysicalPages( GetCurrentProcess(),
&NumberOfPages,
aPFNs );
if( bResult != TRUE )
{
_tprintf(_T("Cannot free physical pages, error %u.\n"), GetLastError());
return;
}
bResult = VirtualFree( lpMemReserved,
0,
MEM_RELEASE );
bResult = HeapFree(GetProcessHeap(), 0, aPFNs);
if( bResult != TRUE )
{
_tprintf(_T("Call to HeapFree has failed (%u)\n"), GetLastError() );
}
}
BOOL
LoggedSetLockPagesPrivilege ( HANDLE hProcess,
BOOL bEnable)
{
struct {
DWORD Count;
LUID_AND_ATTRIBUTES Privilege [1];
} Info;
HANDLE Token;
BOOL Result;
Result = OpenProcessToken ( hProcess,
TOKEN_ADJUST_PRIVILEGES,
& Token);
if( Result != TRUE )
{
_tprintf( _T("Cannot open process token.\n") );
return FALSE;
}
Info.Count = 1;
if( bEnable )
{
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
Info.Privilege[0].Attributes = 0;
}
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;
}
Result = AdjustTokenPrivileges ( Token, FALSE,
(PTOKEN_PRIVILEGES) &Info,
0, NULL, NULL);
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;
}
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;
}
CreateFilemapping
,然后跟随MapViewOfFile
更接近于使用MAP_ANONYMOUS
调用mmap
。在Win32中,使用MapViewOfFileEx
并指定相同的地址最接近mremap
。当然,如果您坚持使用它,也可以使用VirtualAllocEx
。无论如何,您都无法保证获得相同的地址(在Linux下也是如此!),因此我想尽可能接近并不重要。 - DamonMEM_RESERVE
来保留内存空间,然后稍后再使用MEM_COMMIT
来分配实际需要的内存空间,这样可以确保你得到的正是你所需的。唯一需要注意的是在32位系统下可能会耗尽地址空间,如果你需要的是几百兆字节的内存(在64位系统下则没有问题,显然)。 - Damon