生活随笔
收集整理的這篇文章主要介紹了
32位程序使用超过4G的内存 实例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ?最近在做個程序,雖然是小型程序,但是使用的內存量卻很大,動輒達到10G。在64位系統上可以輕松實現,無奈我是基于32位的系統進行開發,程序還沒跑起來就已經被終止了。??
??? 試過很多辦法,包括文件內存映射等,效率不高,而且由于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文件示例:
[xhtml]?view plaincopy
[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以上內存的目的。下圖分別為開啟PAE和未開啟PAE時系統識別出的內存容量區別。?
?
圖一.開啟PAE
?
?
?
圖二.關閉PAE
??? 如果沒有打開PAE,系統只能認出3G的內存,最多可以再多0.5G不到,這樣即使使用AWE,由于系統和其他應用程序已經占去了一部分內存,剩下的內存或許也只有2G多一點了,沒什么太大提高。只有當系統認出了4G以上的內存,AWE才能發揮它真正的作用。
??? 下面我們看看windows中給出的有關AWE的API函數,它們都定義在winbase.h中。
?
[cpp]?view plaincopy
#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 andWINVER |
| 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的代碼出錯,可以在程序頭文件中加入下面的代碼。
[cpp]?view plaincopy
#ifndef?_WIN32_WINNT??#define?_WIN32_WINNT?0x0501??#endif????
?
??? 下面簡要介紹一下每個API的功能。
?
[cpp]?view plaincopy
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。
?
[cpp]?view plaincopy
#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位平臺
總結
以上是生活随笔為你收集整理的32位程序使用超过4G的内存 实例的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。