操作系统课设:内存分配(C++ win10)
要求实现:
- 编写一个程序,创建两个线程,一个用于内存分配,另一个用于跟踪内存的分配情况并打印信息。
- 将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 10 操作系统
- Visual Studio 2017
代码:
//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错误是由于设置了一次性的页面保护。