码迷,mamicode.com
首页 > 其他好文 > 详细

第15章 在应用程序中使用虚拟内存(1)

时间:2015-10-06 18:11:07      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:

15.1 预订(Reserve)地址空区域

(1)VirutalAlloc(pvAddress,dwSize,fdwAllocationType,fdwProtect)

    ①PVOID pvAddress参数:内存地址,要预订的地址空间中的哪一块。

    A.这个参数也说明也该函数是较底层函数的原因,因为可以由我们指定在哪个地址分配内存!但这也是有限制的,即只能在用户模式分区中分配内存,否则将会失败。

    B.预订区域时,系统始终是按CPU的分配粒度来分配区域的。如果指定的pvAddress不是64K的整数倍时,则系统会将pvAddress向下取整到64K的整数倍作为预订区域的起始地址(也叫区域的基地址。如pvAddress=300×65536 + 8192时,会被取整为300×65536。(但应注意:在提交时,起始地址始终(与区域大小一样),始终都是页面大小的整数倍

    C.当该参数为NULL时,系统会自动找一块空闲的区域来预订。但系统不保证一定会从地址空间的底部往上分配,也不保证一定从地址空间的顶部往下分配。

    D.如果在fdwAllocatioType指定MEM_TOP_DOWN时会尽可能地在高地址中分配空间

  ②SIZE_T dwSize参数——用来指定预订区域的大小,以字节为单位

    A.区域大小是以CPU页面大小的整数倍来预订的(一般为4KB)。

    B.如果指定的dwSize不是4KB的整数倍,则会被向上取整。如预订大小为62KB,则最终得到的区域大小为64KB。

    ③DWORD fdwAllocationType参数——要预订还是提交等

类型

备注

MEM_RESERVE

A.预留区域时并没有分配物理存储器(物理存储器指物理内存、页交换文件或文件映像),只是增加了一个描述进程虚拟地址空间使用状态的数据结构,用来记录这个区域己被预订。

B.因没有真正分配物理存储器所以这个区域不能直接访问,否则会引起“内存访问违规”。但“预订”操作的速度相对较快

MEM_COMMIT

A.提交区域,为预订区域调拨后备的物理储存器类型。注意,提交时也并没有立刻从物理内存分配空间,而是提交给页交换文件!

B.直到当第一次访问这段区域时,系统会抛出“缺页错误”并处理该错误,这时才真正映射到物理内存。这种策略叫“demand-paging”策略,这既节省了时间,也节省了内存的浪费。

MEM_PHYSICAL

A.只用在“地址空间扩展(AWE)”中,该标志必须且只能与MEM_RESERVE一起使用。

B.页面必须具有可读写属性(具体该标志的使用见后面的《AWE》一节)

MEM_TOP_DOWN

传NULL给pvAddress,同时对MEM_TOP_DOWN与MEM_RESERVE按位或传给fdwAllocationType可以让系统从尽可能高的内存地址来预订区域。这样可以防止从进程地址空间的中间预订,从而避免引起内存碎片

  ④DWORD fdwProtect参数——给区域指定保护属性

    A.预订区域时,可以使用PAGE_NOACCESS、PAGE_READWRITE、PAGE_READONLY、PAGE_EXECUTE_*等几类。

    B.但预订时不能使用PAGE_WRITECOPY、PAGE_EXECUTE_WRITECOPY、PAGE_GUARD、PAGE_NOCACHE和PAGE_WRITECOMBINE,因为这些标志跟物理存储器有关。

15.2 给区域调拨(Commit)物理存储器

(1)调用VirtualAlloc时传入MEM_COMMIT标志,在给物理存储器指定页面保护属性时,可以与预订时一致,也可以完全不同!但最终的保护性页以物理页面的保护属性为准(而不是区域的保护属性)

(2)在己预订的区域中,可以无须一下子给整个区域都调拨物理存储器,可以用pvAddress和dwSize来指定要想提交的部分。(即哪个内存地址,大小为多少,同时x86中都是以4KB的页面大小为基本单位)

(3)由于系统是基于一个完全页面(4KB大小)来指定保护属性的,所以同一个物理存储页不可能有不同的保护属性。但同一区域中的另一个页面可以是另一种保护属性

15.3 同时预订和调拨物理存储器

(1)调用VirtualAlloc,并传入MEM_RESERVE|MEM_COMMIT标志

(2)大页面内存的分配

  ①返回大页面分配粒度:SIZE_T GetLargePageMinimum(),如果系统不支持大页面,则返回0

  ②调用VirtualAlloc时传入MEM_SERVE|MEM_COMMIT|MEM_LARGE_PAGE,即必须是同时预订和提交内存,而不能分开来实现。

  ③同时要分配的内存块大小(dwSize)必须是大页面分配粒度的整倍数,页面的保护属性fdwProtect也必须指定为PAGE_READWRITE。

【注意】

  ①大页面分配到的内存是不可换页的,也就是必须驻留内存,而不会被换出到页交换文件。所以就要求物理内存页面要被锁定。

  ②默认下,要使用“锁定内存页面”要先得到授权。(方法是“控制面板”→“管理工具”→“本地安全策略”→“本地策略”→“用户权限分配”→“锁定内存页”中添加用户或组)

15.4 何时调拨物理存储器

(1)先预订区域而不提交物理存储器(再次提醒,实际上是提交到页交换文件的)可以节省大量的物理存储器。

(2)当应用程序要访问未提交的内存地址时,会引发异常,系统会通知我们的应用程序。我们要用的就是给应用程序设置一个异常处理程序,在这里面再去真正地执行提交物理存储器的操作,从而实现内存的“按需分配”。

15.5 撤消调拨物理存储器及释放区域

(1)VirtualFree(pvAdress,dwSize,fdwFreeType)

  ①撤消并释放整个区域:pvAddress指定为区域的基地址,即预订区域时VirtualAlloc的返回值。同时dwSize必须传入0,因为系统知道该区域的大小。第3个参数传MEM_RELEASE。(注意此时不能只撤消一部分区域。此外,这时提交的内存被释放且预订的虚拟地址空间也归返这就意味着MEM_RELEASE不能得MEM_DECOMMAND一起使用!

  ②撤消部分物理存储器(但保留虚拟地址空间):指定pvAddress和dwSize,并传入MEM_DECOMMIT。与预订和提交物理存储器一校址,撤消也是基于页面粒度的,即系统会撤消地址空间被pvAddress到pvAddress+dwSize覆盖的所有页面注意,页面是个4KB的空间,即如果pvAddress是位于某个页面中间,那该页面也作为整体被撤消)。

(2)何时撤消调拨物理存储器

  ①由于撤消操作是以页面粒度为基本单位的,当我们进行撤消时,可能会把当前正在使用的某个变量的内存也给撤消掉。

  ②要安全的撤消操作,就要为每个变量或结构体增加一个标志位,用来记录它们正在被使用的情况。只有等到同一个页面所有相邻的结构不在被使用时,才能进行撤消操作。

【DemandPaging程序】利用结构化异常机制进行内存的“按需分配”

技术分享     技术分享

#include <windows.h>
#include <tchar.h>
#include <locale.h>

#define PAGELIMIT 80    //请求页面的总数
LPBYTE lpNextPage=NULL;      //下一次请求的页面地址
DWORD dwPageSize = 0;   //页面的大小(分配粒度)
DWORD dwPages = 0;      //当前己经请求的页面数量

INT PageFaultExceptionFilter(DWORD dwCode){
    LPVOID lpvResult;

    //如果不是页面错误,则退出
    if (dwCode !=EXCEPTION_ACCESS_VIOLATION){
        _tprintf(_T("错误发生[%d]\n"), dwCode);
        return EXCEPTION_EXECUTE_HANDLER; //当错误发生时,执行except代码块中的内容。
    }
    
    _tprintf(_T("页面访问错误,"));

    if (dwPages >=PAGELIMIT){
        _tprintf(_T("超出页面数量%d\n"), PAGELIMIT);
        
        return EXCEPTION_EXECUTE_HANDLER; //当错误发生时,执行except代码块中的内容。
    }

    lpvResult = VirtualAlloc((LPVOID)lpNextPage, dwPageSize, MEM_COMMIT, PAGE_READWRITE);
    if (lpvResult == NULL){
        _tprintf(_T("提交新页面失败!\n"));
        return EXCEPTION_EXECUTE_HANDLER;
    } else{
        _tprintf(_T("将重新提交一个新页面!\n"));
    }

    dwPages++;
    lpNextPage += dwPageSize;
    return EXCEPTION_CONTINUE_EXECUTION; //当错误发生时,执行except代码块后面的内容。
}

VOID ErrorExit(LPTSTR oops){
    _tprintf(_T("错误!%s,出错代码为%ld\n"), 
             oops, GetLastError());
    exit(0);
}

int _tmain(){
    _tsetlocale(LC_ALL, _T("chs"));

    LPVOID lpvBase; //要测试的内存基地址
    LPTSTR lpPtr;   //通用的字符指针
    BOOL bSuccess;  //
    SYSTEM_INFO si; //系统信息结构体
    GetSystemInfo(&si);
    dwPageSize = si.dwPageSize;

    _tprintf(_T("CPU页面大小为%dKB.\n"), si.dwPageSize/1024);

    //在进程的虚拟空间中预订页面
    lpvBase = VirtualAlloc(NULL,   //系统自动选择基地址
                           PAGELIMIT*dwPageSize,//区域的大小
                           MEM_RESERVE, //预订(注意不是提交)
                           PAGE_NOACCESS);//保护属性,不可读写
    
    if (lpvBase == NULL){
        ErrorExit(_T("预订页面失败"));
    }

    lpPtr =(LPTSTR)lpvBase;
    lpNextPage = (LPBYTE)lpvBase;
    for (DWORD i = 0; i < PAGELIMIT*dwPageSize/sizeof(TCHAR);i++){
        __try{
            //写入内存
            lpPtr[i] = _T(a); //因未提交物理存储器,这里会引发异常
        }
        //如果发生页面错误,则提交另一个页面并尝试继续
        __except (PageFaultExceptionFilter(GetExceptionCode())){
            //以下的代码只有在“过滤函数”中提交下个页面失败时
            //才会被调用
            ExitProcess(GetLastError());
        }
    }

    //释放区域
    bSuccess = VirtualFree(lpvBase, 
                           0,  //当使用MEM_RELEASE时,必须为0
                           MEM_RELEASE);//撤消提交的区域(这里不用加MEM_DECOMMIT)

    _tprintf(_T("释放操作%s.\n"), bSuccess ? _T("成功") : _T("失败"));
    
    return 0;
}

【VMAlloc程序】内存的垃极收集(VirtualFree函数的应用)

 

第15章 在应用程序中使用虚拟内存(1)

标签:

原文地址:http://www.cnblogs.com/5iedu/p/4857446.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!