轉載自:http://blog.csdn.net/jerjupiter/article/details/4577083數據庫
最近在作個程序,雖然是小型程序,可是使用的內存量卻很大,動輒達到10G。在64位系統上能夠輕鬆實現,無奈我是基於32位的系統進行開發,程序還沒跑起來就已經被終止了。 windows
試過不少辦法,包括文件內存映射等,效率不高,並且因爲32位應用程序的限制,可用的內存地址最高只能到0x7FFFFFFF,能調用的內存到2G就是極限了。最後好不容易找到了AWE(Address Windowing Extensions)。 數組
AWE是Windows的內存管理功能的一組擴展,它容許應用程序獲取物理內存,而後將非分頁內存的視圖動態映射到32位地址空間。雖然32位地址空間限制爲4GB,可是非分頁內存卻能夠遠遠大於4GB。這使須要大量內存的應用程序(如大型數據庫系統)能使用的內存量遠遠大於32位地址空間所支持的內存量。 安全
與AWE有關的函數在後面介紹。 服務器
爲了使用大容量內存,除了要用到AWE外,還有同樣東西不能少,那就是PAE(Physical Address Extension)。PAE是基於x86的服務器的一種功能,它使運行Windows Server 2003,Enterprise Edition 和Windows Server 2003,Datacenter Edition 的計算機能夠支持 4 GB 以上物理內存。物理地址擴展(PAE)容許將最多64 GB的物理內存用做常規的4 KB頁面,並擴展內核能使用的位數以將物理內存地址從 32擴展到36。 函數
通常狀況下,windows系統的PAE沒有生效,只有開啓了PAE後windows系統才能夠識別出4G以上的內存。在使用boot.int的系統中,要啓動PAE必須在boot.ini中加入/PAE選項。在Windows Vista和Windows7中則必須修改內核文件,同時設置BCD啓動項。針對Vista系統和Win7系統可使用Ready For 4GB這個軟件直接完成這一操做,具體方法見Ready For 4GB的軟件說明。如下就是一個開啓了/PAE選項的boot.ini文件示例: 工具
[boot loader] timeout=30 default=multi(0)disk(0)rdisk(0)partition(1)WINDOWS [operating systems] multi(0)disk(0)rdisk(0)partition(1)WINDOWS="Windows Server 2003, Enterprise" /fastdetect /PAE
本文將以Windows 7旗艦版爲例介紹如何在打開PAE的狀況下使用AWE在程序中達到使用2G以上內存的目的。post
若是沒有打開PAE,系統只能認出3G的內存,最多能夠再多0.5G不到,這樣即便使用AWE,因爲系統和其餘應用程序已經佔去了一部份內存,剩下的內存或許也只有2G多一點了,沒什麼太大提升。只有當系統認出了4G以上的內存,AWE才能發揮它真正的做用。測試
下面咱們看看windows中給出的有關AWE的API函數,它們都定義在winbase.h中。ui
#if (_WIN32_WINNT >= 0x0500) // // Very Large Memory API Subset // WINBASEAPI BOOL WINAPI AllocateUserPhysicalPages( __in HANDLE hProcess, __inout PULONG_PTR NumberOfPages, __out_ecount_part(*NumberOfPages, *NumberOfPages) PULONG_PTR PageArray ); WINBASEAPI BOOL WINAPI FreeUserPhysicalPages( __in HANDLE hProcess, __inout PULONG_PTR NumberOfPages, __in_ecount(*NumberOfPages) PULONG_PTR PageArray ); WINBASEAPI BOOL WINAPI MapUserPhysicalPages( __in PVOID VirtualAddress, __in ULONG_PTR NumberOfPages, __in_ecount_opt(NumberOfPages) PULONG_PTR PageArray ); //... #endif
從winbase.h中的定義能夠看出,只有當你的系統版本大於或等於0x0500時,纔可以使用AWE。各個版本的_WIN32_WINNT值見下表,Windows 2000如下的版本不能使用AWE。
Minimum system required |
Minimum value for _WIN32_WINNT and WINVER |
Windows 7 |
0x0601 |
Windows Server 2008 |
0x0600 |
Windows Vista |
0x0600 |
Windows Server 2003 with SP1, Windows XP with SP2 |
0x0502 |
Windows Server 2003, Windows XP |
0x0501 |
Windows 2000 |
0x0500 |
若是你的系統版本符合要求,可是編譯器在編譯加入了AWE API的代碼出錯,能夠在程序頭文件中加入下面的代碼
#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif
下面簡要介紹一下每一個API的功能。
BOOL WINAPI AllocateUserPhysicalPages( //分配物理內存頁,用於後面AWE的內存映射 __in HANDLE hProcess, //指定可使用此函數分配的內存頁的進程 __inout PULONG_PTR NumberOfPages, //分配的內存頁數,頁的大小由系統決定 __out PULONG_PTR UserPfnArray //指向存儲分配內存頁幀成員的數組的指針 ); BOOL WINAPI FreeUserPhysicalPages( //釋放AllocateUserPhysicalPages函數分配的內存 __in HANDLE hProcess, //釋放此進程虛擬地址空間中的分配的內存頁 __inout PULONG_PTR NumberOfPages, //要釋放的內存頁數 __in PULONG_PTR UserPfnArray //指向存儲內存頁幀成員的數組的指針 ); BOOL WINAPI MapUserPhysicalPages( //將分配好的內存頁映射到指定的地址 __in PVOID lpAddress, //指向要重映射的內存區域的指針 __in ULONG_PTR NumberOfPages, //要映射的內存頁數 __in PULONG_PTR UserPfnArray //指向要映射的內存頁的指針 );
在看實例程序前還有一些設置須要作,須要對系統的本地安全策略進行設置。在win7中,打開「控制面板->系統和安全->管理工具->本地安全策略」,給「鎖定內存頁」添加當前用戶,而後退出,重啓(不重啓通常沒法生效!)。
通過前面的準備(再囉嗦一次:確認本身的電腦裝有4G或4G以上的內存;開啓PAE,使系統認出4G或以上的內存;設置好本地安全策略),咱們就能夠經過下面的代碼來作個實驗了。
代碼是從MSDN中AWE的一個Example修改而來的,具體流程見代碼中的註釋,若是對該Example的源代碼有興趣能夠參考MSDN。
#include "AWE_TEST.h" #include <windows.h> #include <stdio.h> #define MEMORY_REQUESTED ((2*1024+512)*1024*1024) //申請2.5G內存,測試機上只有4G內存,並且系統是window7,比較佔內存.申請3G容易失敗. #define MEMORY_VIRTUAL 1024*1024*512 //申請長度0.5G的虛擬內存,即AWE窗口. //檢測"鎖定內存頁"權限的函數 BOOL LoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable); void _cdecl main() { BOOL bResult; // 通用bool變量 ULONG_PTR NumberOfPages; // 申請的內存頁數 ULONG_PTR NumberOfPagesInitial; // 初始的要申請的內存頁數 ULONG_PTR *aPFNs; // 頁信息,存儲獲取的內存頁成員 PVOID lpMemReserved; // AWE窗口 SYSTEM_INFO sSysInfo; // 系統信息 INT PFNArraySize; // PFN隊列所佔的內存長度 GetSystemInfo(&sSysInfo); // 獲取系統信息 printf("This computer has page size %d./n", sSysInfo.dwPageSize); //計算要申請的內存頁數. NumberOfPages = MEMORY_REQUESTED/sSysInfo.dwPageSize; printf ("Requesting %d pages of memory./n", NumberOfPages); // 計算PFN隊列所佔的內存長度 PFNArraySize = NumberOfPages * sizeof (ULONG_PTR); printf ("Requesting a PFN array of %d bytes./n", PFNArraySize); aPFNs = (ULONG_PTR *) HeapAlloc(GetProcessHeap(), 0, PFNArraySize); if (aPFNs == NULL) { printf ("Failed to allocate on heap./n"); return; } // 開啓"鎖定內存頁"權限 if( ! LoggedSetLockPagesPrivilege( GetCurrentProcess(), TRUE ) ) { return; } // 分配物理內存,長度2.5GB NumberOfPagesInitial = NumberOfPages; bResult = AllocateUserPhysicalPages( GetCurrentProcess(), &NumberOfPages, aPFNs ); if( bResult != TRUE ) { printf("Cannot allocate physical pages (%u)/n", GetLastError() ); return; } if( NumberOfPagesInitial != NumberOfPages ) { printf("Allocated only %p pages./n", NumberOfPages ); return; } // 保留長度0.5GB的虛擬內存塊(這個內存塊即AWE窗口)的地址 lpMemReserved = VirtualAlloc( NULL, MEMORY_VIRTUAL, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE ); if( lpMemReserved == NULL ) { printf("Cannot reserve memory./n"); return; } char *strTemp; for (int i=0;i<5;i++) { // 把物理內存映射到窗口中來 // 分5次映射,每次映射0.5G物理內存到窗口中來. // 注意,在整個過程當中,lpMenReserved的值都是不變的 // 可是映射的實際物理內存倒是不一樣的 // 這段代碼將申請的2.5G物理內存分5段依次映射到窗口中來 // 並在每段的開頭寫入一串字符串. bResult = MapUserPhysicalPages( lpMemReserved, NumberOfPages/5, aPFNs+NumberOfPages/5*i); if( bResult != TRUE ) { printf("MapUserPhysicalPages failed (%u)/n", GetLastError() ); return; } // 寫入字符串,雖然是寫入同一個虛存地址, // 可是窗口映射的實際內存不一樣,因此是寫入了不一樣的內存塊中 strTemp=(char*)lpMemReserved; sprintf(strTemp,"This is the %dth section!",i+1); // 解除映射 bResult = MapUserPhysicalPages( lpMemReserved, NumberOfPages/5, NULL ); if( bResult != TRUE ) { printf("MapUserPhysicalPages failed (%u)/n", GetLastError() ); return; } } // 如今再從5段內存中讀出剛纔寫入的字符串 for (int i=0;i<5;i++) { // 把物理內存映射到窗口中來 bResult = MapUserPhysicalPages( lpMemReserved, NumberOfPages/5, aPFNs+NumberOfPages/5*i); if( bResult != TRUE ) { printf("MapUserPhysicalPages failed (%u)/n", GetLastError() ); return; } // 將映射到窗口中的不一樣內存塊的字符串在屏幕中打印出來 strTemp=(char*)lpMemReserved; printf("%s/n",strTemp); // 解除映射 bResult = MapUserPhysicalPages( lpMemReserved, NumberOfPages/5, NULL ); if( bResult != TRUE ) { printf("MapUserPhysicalPages failed (%u)/n", GetLastError() ); return; } } // 釋放物理內存空間 bResult = FreeUserPhysicalPages( GetCurrentProcess(), &NumberOfPages, aPFNs ); if( bResult != TRUE ) { printf("Cannot free physical pages, error %u./n", GetLastError()); return; } // 釋放虛擬內存地址 bResult = VirtualFree( lpMemReserved, 0, MEM_RELEASE ); // 釋放PFN隊列空間 bResult = HeapFree(GetProcessHeap(), 0, aPFNs); if( bResult != TRUE ) { printf("Call to HeapFree has failed (%u)/n", GetLastError() ); } } /***************************************************************** 輸入: HANDLE hProcess: 須要得到權限的進程的句柄 BOOL bEnable: 啓用權限 (TRUE) 或 取消權限 (FALSE)? 返回值: TRUE 表示權限操做成功, FALSE 失敗. *****************************************************************/ 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 ) { printf( "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; } // 得到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; } // 修改權限 Result = AdjustTokenPrivileges ( Token, FALSE, (PTOKEN_PRIVILEGES) &Info, 0, NULL, NULL); // 檢查修改結果 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; }
程序運行後,能夠看出系統分頁的大小爲4K,總共申請了655360個分頁,也就是2.5G。每一個分頁成員佔4字節,總共2621440字節。2.5G內存分紅5段512M的塊,成功寫入了字符串併成功讀取。
在調試過程當中,在執行了AllocateUserPhysicalPages函數後設置斷點,查看任務管理器,能夠看出成功分配了物理內存後,實際物理內存被佔用了2.5G,從而驗證了AWE的效果。
經過上述示例,咱們成功的在32位系統中識別出了4G的內存,而且在32位程序中成功使用了超過2G的內存。藉助PAE和AWE,即便在32位系統上,咱們也可以順利開發對內存消耗較大的應用程序,而不須要依賴於64位平臺。