3

据我了解,要在 linux 中保留一块虚拟内存,您可以mmap使用MAP_ANONYMOUSandMAP_PRIVATE调用,而 windows 上的等效系统调用是VirtualAlloc.

但是,linux提供mremap了调整内存映射的大小,手册页说

更改虚拟地址和内存页之间的映射

我找不到等效的 Windows 系统调用。似乎要重新分配内存,必须使用HeapAlloc而不是VirtualAlloc,然后使用HeapReAlloc。关于HeapReAlloc,msdn 说

保存内存内容的过程涉及可能非常耗时的内存复制操作。

那么有没有办法在 Windows 中重新映射虚拟内存?如果不是,为什么不呢?

4

3 回答 3

3

Windows 上虚拟内存的细粒度控制可以通过 Win32 API 中的 AWE 系列函数来实现。

初始分配是使用AllocateUserPhysicalPages完成的,顾名思义,它会为您分配实际的物理页面。然后,您可以继续使用MapUserPhysicalPages将这些物理页面映射到虚拟页面到您之前使用VirtualAlloc. 请注意,您可以将已映射的物理页面重新映射到不同的虚拟页面。

当您同时处理物理和虚拟内存时,这确实带有一组不同的语义。一些可能值得一提的缺点是您需要确保不存在混叠;并且您实际上被限制为使用本机页面大小,即您将无法使用大页面。

于 2016-07-21T11:21:47.900 回答
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;
    }
于 2020-08-17T18:11:01.233 回答
0

您可以使用地址窗口扩展 (AWE) 来做到这一点。例如,您可以保留两个虚拟内存区域,然后将它们一个接一个地映射到同一个物理区域。或者您可以保留单个虚拟区域但映射它的不同部分。

它不如 Linux mremap 方便,但它可以工作。

请查看地址窗口扩展 (AWE): https ://msdn.microsoft.com/en-us/library/windows/desktop/aa366531(v=vs.85).aspx

我的工作代码基于 MSDN 示例供您参考。在运行它之前,请记住在 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;
}
于 2015-11-01T21:06:35.157 回答