要求實現:
- 編寫一個程序,創建兩個線程,一個用於內存分配,另一個用於跟蹤內存的分配情況並打印信息。
- 將VirtualAlloc函數的參數ftAllocahonType分別改為MEM_RESET或MEM_TOP_DOWN,將nProtect參數分別改為PAGE_GUARD、PAGE_NOACCESS或PAGE_NOCACHE,再進行本實驗的各項操作,以及查看內存分配的各個結果,分析原因。
- 嘗試調換分配、回收、內存復位、加鎖、解鎖、提交、回收的次序,查看結果,並分析原因。
個人認為的一些注意事項:
- 由於我們目前用的都是64位的windows,並且電腦內存都比較大(E.G我的電腦為16GB),故要注意將測試程序編譯為64位,否則可能看不到效果。(個人認為:可能是由於32位程序最大內存尋址空間就為4GB,只要剩余內存大於4GB,可能就會導致avaiable永遠顯示為一個定值)。
- 由於我們是通過查看全局的內存變化,來看運行效果的,故要盡量分配多的內存,來抵消去當前的操作系統的一些后台內存分配變化,來看出效果。
代碼:
//windows 內存分配. windows/visual stdio*/cl xxx.cpp #include <windows.h> #include <stdio.h> #include <time.h> #include <tchar.h> #include <stdlib.h> #include <iostream> #include <iomanip> using namespace std; DWORD dwID; MEMORYSTATUS memInfo; #define C(I, S) CreateSemaphore(NULL, (I), 3, (S)) #define P(S) WaitForSingleObject((S), INFINITE) #define V(S) ReleaseSemaphore((S), 1, NULL) #define CT(func, args) CreateThread(NULL, 0, (func), (args), 0, &dwID) #define PM(num, S) WaitForMultipleObjects((num), (S), true, INFINITE) DWORD WINAPI Tracer(LPVOID lpParam); HANDLE hThread; HANDLE s1, s2; LPVOID lpvBase; //整個大內存基址 LPTSTR lpNxtPage; //下一個頁面地址 LPTSTR lpPtr; //字符指針,用於測試訪問,賦值 DWORD PageSize; //頁面大小 DWORD showSysInfo() { SYSTEM_INFO siSysInfo; GetSystemInfo(&siSysInfo); cout << "---------------BEG SYSTEM_INFO---------------" << endl; cout << "OEM ID: " << siSysInfo.dwOemId << endl; cout << "Number of processors: " << siSysInfo.dwNumberOfProcessors << endl; cout << "Page size: " << siSysInfo.dwPageSize << endl; cout << "Processor type: " << siSysInfo.dwProcessorType << endl; cout << "Minimum application address: " << siSysInfo.lpMinimumApplicationAddress << endl; cout << "Maximum application address: " << siSysInfo.lpMaximumApplicationAddress << endl; cout << "---------------END SYSTEM_INFO---------------\n\n"; return siSysInfo.dwPageSize; } void showMemStatus(const char* s) { GlobalMemoryStatus(&memInfo); cout <<"Status: " << s << endl; cout << hex << "dwAvailPhys: 0x" << memInfo.dwAvailPhys << ", dwAvailPageFile: 0x" << memInfo.dwAvailPageFile << ", dwAvailVirtual: 0x" << memInfo.dwAvailVirtual << endl << endl; //memInfo_prev = memInfo; } DWORD WINAPI Tracer(LPVOID lpParam) { for(int i = 0; i < 3; ++i) { P(s2); showMemStatus("提交頁面了並且賦值了"); V(s1); } return 0; } INT PageFaultExceptionFilter(DWORD dwCode) { LPVOID lpvResult; cout << "訪問異常, 錯誤代碼 = 0x" << dwCode << endl << endl; lpvResult = VirtualAlloc((LPVOID)lpNxtPage, 10000*PageSize, MEM_COMMIT, PAGE_READWRITE); if (lpvResult == NULL) { cout << "VirtualAlloc failed" << endl << endl; return EXCEPTION_EXECUTE_HANDLER; } else { showMemStatus("剛剛提交完頁面"); } lpNxtPage = (LPTSTR) ((PCHAR) lpNxtPage + 10000*PageSize); return EXCEPTION_CONTINUE_EXECUTION; } int main() { PageSize = showSysInfo(); s1 = C(1, "ss1"); s2 = C(0, "ss2"); hThread = CT(Tracer, NULL); showMemStatus("之前"); lpvBase = VirtualAlloc(NULL, 30000*PageSize, MEM_RESERVE, PAGE_NOACCESS); //先設置noacess, 賦值會出錯,執行PageFaultExceptionFilter // cout <<"Status: %s\n", s); if (lpvBase == NULL) { cout << GetLastError() << endl; return EXCEPTION_EXECUTE_HANDLER; } else { cout << "成功初始化保留頁面" << endl; } showMemStatus("初始化: 保留頁面"); lpPtr = lpNxtPage = (LPTSTR) lpvBase; for(int i = 0;i < 3; ++i) { P(s1); for(int j = 0; j < 10000*PageSize; ++j) { __try { //測試給申請的內存賦值 lpPtr[10000*i*PageSize + j] = 'a'; } __except (PageFaultExceptionFilter(GetExceptionCode())) { _tprintf (TEXT("出錯了")); ExitProcess( GetLastError() ); } } V(s2); } cout << "第一個頁面內容: " << lpPtr[3] << endl << endl; P(s1); LPVOID lpvResult = VirtualAlloc(NULL, PageSize, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE | PAGE_GUARD); if (lpvResult == NULL) { cout << GetLastError() << endl; return EXCEPTION_EXECUTE_HANDLER; } else { cout << "已經成功提交了一個頁面\n\n"; } //測試一次性的PAGE_GUARD功能!同時測試鎖定 BOOL bLocked = VirtualLock(lpvResult, PageSize); if (!bLocked) { cout << "不能鎖定 " << lpvResult << ", 錯誤代碼 = " << GetLastError() << "\n\n"; } else { cout << "成功鎖定了 " << lpvResult << "\n\n"; } bLocked = VirtualLock(lpvResult, PageSize); if (!bLocked) { cout << "不能鎖定 " << lpvResult << ", 錯誤代碼 = " << GetLastError() << "\n\n"; } else { cout << "成功鎖定了 " << lpvResult << "\n\n"; } showMemStatus("鎖定了一個頁面"); //測試解鎖 bLocked = VirtualUnlock(lpvResult, PageSize); if (!bLocked) { cout << "不能解鎖鎖定 " << lpvResult << ", 錯誤代碼 = " << GetLastError() << "\n\n"; } else { cout << "成功鎖定了 " << lpvResult << "\n\n"; } showMemStatus("解鎖完一個頁面"); //測試reset LPVOID lpvResult1 = VirtualAlloc(lpvResult, PageSize, MEM_RESET, PAGE_READWRITE); if (lpvResult == NULL) { cout << GetLastError() << endl; return EXCEPTION_EXECUTE_HANDLER; } else { cout << "已經成功Reset了一個頁面\n\n"; } showMemStatus("Reset了一個頁面"); //測試釋放, 整個,物理+頁面虛擬 BOOL bSuccess = VirtualFree(lpvBase, 0, MEM_RELEASE); if (!bSuccess) { cout << "錯誤代碼 = " << GetLastError() << "\n\n"; } else { cout << "成功釋放了 " << lpvBase << "\n\n"; } showMemStatus("釋放了大內存"); P(hThread); return 0; }
運行結果與分析:

首先看可用內存dwAvailPhys,可見在保留,提交頁面的時候,可供內存沒有大的變化減少,但是一旦對提交的區域賦值,內存顯著減少。
故得出結論,VirtualAlloc 只有在真正賦值使用的時候才分配真正的內存。
其次看PageFile, 在每次頁面提交的時候減少。
而Virtual是在第一次分配的時候減少,無論保留還是提交。
c0000005錯誤是由於設置了PAGE_NOACCESS,80000001錯誤是由於設置了一次性的頁面保護。
