AWE 内存管理

AWE (Address Windows Extension) 可以使用开启 PAE 后普通应用程序无法使用到的内存,这部分内存系统可能无法识别,但通过 AWE 则可以完美访问。操作 AWE 内存的具体步骤如下,大部分内容来自北风网视频教程。

1、开窗 VirtualAlloc + MEM_PHYSICAL,明确告知系统,这段保留的空间未来将存放我自己申请的物理内存。
2、分配物理内存 AllocateUserPhysicalPages,按页面个数来分配,不是按字节分配的,最少一个页面 4K 的物理内存。
3、将申请好的物理内存映射到窗口中(相当于提交)MapUserPhysicalPages。
4、对已经提交的内存读写…
5、释放物理内存页面。

注意事项

1、打开 PAE 后并不是所有应用程序都可以访问大于 4GB 的内存,要访问需要通过 AWE 打开 PAE 高出 4GB 地址空间的内存,AWE 但不仅限于打开 PAE 后多出来的内存,当需要自己操作物理页面的时候,都可以用到它。(顺网无盘的产品利用系统未识别的内存做缓存就是这个原理)
2、使用 AWE 一样需要锁定内存页的权限。

代码及配图

图中表示了下面代码对内存操作的步骤:

2016-04-13_225214

#define _WIN32_WINNT 0x0502
#include <tchar.h>
#include <windows.h>
#include <stdio.h>

#define MEMORY_REQUESTED 1024 * 1024 * 1024 //1GB

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 *aPFNs1;               // page info; holds opaque data
    ULONG_PTR *aPFNs2;               // page info; holds opaque data
    PVOID lpMemReserved;            // AWE window
    SYSTEM_INFO sSysInfo;           // useful system information
    int PFNArraySize;               // memory to request for PFN array
    TCHAR* pszData;
    TCHAR  pszReadData[100];

    MEMORYSTATUSEX ms = { sizeof(MEMORYSTATUSEX) };
    GlobalMemoryStatusEx(&ms);

    //  LoggedSetLockPagesPrivilege(::GetCurrentProcess(),TRUE);

    //1 "开窗"
    lpMemReserved = VirtualAlloc(NULL, MEMORY_REQUESTED, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
    //计算需要的物理页面大小 以及物理页面需要的页表数组大小
    GetSystemInfo(&sSysInfo);  // fill the system information structure
    NumberOfPages = (MEMORY_REQUESTED + sSysInfo.dwPageSize - 1) / sSysInfo.dwPageSize;
    PFNArraySize = NumberOfPages * sizeof(ULONG_PTR);
    //2 准备物理页面的页表数组数据
    aPFNs1 = (ULONG_PTR *)HeapAlloc(GetProcessHeap(), 0, PFNArraySize);
    aPFNs2 = (ULONG_PTR *)HeapAlloc(GetProcessHeap(), 0, PFNArraySize);
    NumberOfPagesInitial = NumberOfPages;
    //3 分配物理页面
    bResult = AllocateUserPhysicalPages(GetCurrentProcess(), &NumberOfPages, aPFNs1);
    bResult = AllocateUserPhysicalPages(GetCurrentProcess(), &NumberOfPages, aPFNs2);
    //4 映射第一个1GB到保留的空间中
    bResult = MapUserPhysicalPages(lpMemReserved, NumberOfPages, aPFNs1);
    pszData = (TCHAR*)lpMemReserved;
    _tcscpy(pszData, _T("这是第一块物理内存"));
    //5 映射第二个1GB到保留的空间中
    bResult = MapUserPhysicalPages(lpMemReserved, NumberOfPages, aPFNs2);
    _tcscpy(pszData, _T("这是第二块物理内存"));
    //6 再映射回第一块内存,并读取开始部分
    bResult = MapUserPhysicalPages(lpMemReserved, NumberOfPages, aPFNs1);
    _tcscpy(pszReadData, pszData);
    //7 取消映射
    bResult = MapUserPhysicalPages(lpMemReserved, NumberOfPages, NULL);
    //8 释放物理页面
    bResult = FreeUserPhysicalPages(GetCurrentProcess(), &NumberOfPages, aPFNs1);
    bResult = FreeUserPhysicalPages(GetCurrentProcess(), &NumberOfPages, aPFNs2);
    //9 释放保留的"窗口"空间
    bResult = VirtualFree(lpMemReserved, 0, MEM_RELEASE);
    //10 释放页表数组
    bResult = HeapFree(GetProcessHeap(), 0, aPFNs1);
    bResult = HeapFree(GetProcessHeap(), 0, aPFNs2);

    _tsystem(_T("PAUSE"));

}

/*****************************************************************
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)
    {
        printf("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)
    {
        printf("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)
    {
        printf("Cannot adjust token privileges (%u)\n", GetLastError());
        return FALSE;
    }
    else
    {
        if (GetLastError() != ERROR_SUCCESS)
        {
            printf("Cannot enable the SE_LOCK_MEMORY_NAME privilege; ");
            printf("please check the local policy.\n");
            return FALSE;
        }
    }

    CloseHandle(Token);

    return TRUE;
}

发表评论