操作系統:內存分配(C語言 winapi)


要求實現:

  1. 編寫一個程序,創建兩個線程,一個用於內存分配,另一個用於跟蹤內存的分配情況並打印信息。
  2. VirtualAlloc函數的參數ftAllocahonType分別改為MEM_RESET或MEM_TOP_DOWN,將nProtect參數分別改為PAGE_GUARD、PAGE_NOACCESS或PAGE_NOCACHE,再進行本實驗的各項操作,以及查看內存分配的各個結果,分析原因。
  3. 嘗試調換分配、回收、內存復位、加鎖、解鎖、提交、回收的次序,查看結果,並分析原因。

個人認為的一些注意事項:

  • 由於我們目前用的都是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錯誤是由於設置了一次性的頁面保護。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM