操作系统:内存分配(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错误是由于设置了一次性的页面保护。

优质内容筛选与推荐>>
1、列表页面导航栏链接点击后变色
2、UITableView当数据很少的时候,去掉多余的cell分割线
3、C++大会的入场卷已入手
4、css选择器
5、HDU 6590 Code (判断凸包相交)


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号