Windows编程--虚拟内存的使用

Windows提供了3种进行内存管理的方法,它们是:

虚拟内存,最适合用来管理大型对象或结构数组。

内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行的多个进程之间共享数据。

内存堆栈,最适合用来管理大量的小对象。

 

在地址空间中保留一个区域

通过调用VirtualAlloc函数,可以在进程的地址空间中保留一个区域:

PVOID VirtualAlloc(

PVOID pvAddress,
// 指定内存开始的地址。

SIZE_T dwSize,
// 是分配内存的大小。

DWORD fdwAllocationType,
// 是分配内存的类型。

DWORD fdwProtect);
//访问这块分配内存的权限。


第一个参数pvAddress包含一个内存地址,用于设定想让系统将地址空间保留在什么地方。在大多数情况下,你为该参数传递NULL。它告诉VirtualAlloc保存着一个空闲地址区域的记录的系统应该将区域保留在它认为合适的任何地方。系统可以从进程的地址空间的任何位置来保留一个区域,因为不能保证系统可以从地址空间的底部向上或者从上面向底部来分配各个区域。可以使用MEM_TOP_DOWN标志来说明该分配方式。 

 

 

注意,pvAddress参数传递的任何地址必须始终位于进程的用户方式分区中,否则对VirtualAlloc函数的调用就会失败,导致它返回NULL 

地址空间区域总是按照分配粒度的边界来保留的.

 

第二个参数是dwSize,用于设定想保留的区域的大小以字节为计量单位)。系统保留的区域始终必须是CPU页面大小的倍数.

 

第三个参数是fdwAllocationType,它能够告诉系统你想保留一个区域还是提交物理存储器(这样的区分是必要的,因为VirtualAlloc函数也可以用来提交物理存储器)。若要保留一个地址空间区域,必须传递MEM_RESERVE标识符作为FdwAllocationType参数的值。 

 

如果保留的区域预计在很长时间内不会被释放,那么可以在尽可能高的内存地址上保留该区域。在进程的中间位进行保留有可能导致进程区域的碎片。如果想让系统在最高内存地址上保留一个区域,必须为pvAddress参数和fdwA llocationType 参数传递NULL,还必须逐位使用ORMEM_TOP_DOWN标志和MEM_RESERVE标志连接起来 

 

最后一个参数是fdwProtect,用于指明应该赋予该地址空间区域的保护属性。与该区域相关联的保护属性对映射到该区域的已提交内存没有影响。 

当保留一个区域时,应该为该区域赋予一个已提交内存最常用的保护属性。例如,如果打算提交的物理存储器的保护属性是PAGE_READWRITE(这是最常用的保护属性),那么应该用PAGE _READWRITE保护属性来保留该区域。当区域的保护属性与已提交内存的保护属性相匹配时,系统保存的内部记录的运行效率最高。 

可以使用下列保护属性中的任何一个: PAGE_NOACCESSPAGE_READWRITEPAGE_R EADONLPAGE_EXECUTEPAGE_EXECUTE_READPAGE_EXECUTE_READWRITE。但是,既不能设定PAGE_WRITECOPY属性,也不能设定PAGE_EXECUTE_WRITECOPY属性。如果设定了这些属性,VirtualAlloc函数将不保留该区域,并且返回NULL。另外,当保留地址空间区域时,不能使用保护属性标志PAGE_GUARDPAGE_NOCACHEPAGE_WRITECOMBINE,这些标志只能用于已提交的内存。(关于保护属性见虚拟内存基础或书上P321 

 

 

在保留区域中的提交存储器 

当保留一个区域后,必须将物理存储器提交给该区域,然后才能访问该区域中包含的内存地址。系统从它的页文件中将已提交的物理存储器分配给一个区域。物理存储器总是按页面边界和页面大小的块来提交的。

若要提交物理存储器,必须再次调用VirtualAlloc函数。不过这次为fdwAllocationType参数传递的是MEM_COMMI T标志,而不是MEM_RESERE标志。传递的页面保护属性通常与调用VirtualAlloc来保留区域时使用的保护属性相同(大多数情况下是PAGE_READWRIT E),不过也可以设定一个不同的保护属性。

在已保留的区域中,你必须告诉VirtualAlloc函数,你想将物理存储器提交到何处,以及要提交多少物理存储器。为了做到这一点,可以在pvAddress参数中设定你需要的内存地址,并在dwSize参数中设定物理存储器的数量(以字节为计量单位)。注意,不必立即将物理存储器提交给整个区域 

 

同时进行区域的保留和内存的提交 

有时你可能想要在保留区域的同时,将物理存储器提交给它。只需要一次调用VirtualAlloc函数就能进行这样的操作,如下所示:

 

PVOID pvMem = VirtualAlloc(NULL, 99 * 1024,
MEM_RESERVE
| MEM_COMMIT, PAGE_READWRITE);

 

回收虚拟内存和释放地址空间区域 

 

若要回收映射到一个区域的物理存储器,或者释放这个地址空间区域,可调用VirtualFree函数:

 

BOOL VirtualFree(
LPVOID pvAddress,
SIZE_T dwSize,
DWORD fdwFreeType);

 

当你的进程不再访问区域中的物理存储器时,就可以释放整个保留的区域和所有提交给该区域的物理存储器,

释放区域和物理存储器

pvAddress参数必须是该区域的基地址。此地址与该区域被保留时VirtualAlloc函数返回的地址相同。系统知道在特定内存地址上的该区域的大小,因此可以为dwSize参数传递0。实际上,必须为dwSize参数传递0,否则对VirtualFree的调用就会失败。对于第三个参数fdwFreeType,必须传递MEM_RELEASE,以告诉系统将所有映射的物理存储器提交给该区域并释放该区域。当释放一个区域时,必须释放该区域保留的所有地址空间

 

当想要从一个区域回收某些物理存储器但是却不释放该区域时

VirtualFree函数的pvAddress参数中传递用于标识要回收的第一个页面的内存地址,还必须在dwSize参数中设定要释放的字节数,并在fdwFreeType参数中传递MEM_DECOMMIT标志。

与提交物理存储器的情况一样,回收时也必须按照页面的分配粒度来进行。这就是说,设定页面中间的一个内存地址就可以回收整个页面。当然,如果pvAddress + dwSize的值位于一个页面的中间,那么包含该地址的整个页面将被回收。因此位于pvAddress pvAddress +dwSize范围内的所有页面均被回收。

如果dwSize0pvSddress是已分配区域的基地址,那么Vi rtualFree将回收全部范围内的已分配页面。当物理存储器的页面已经回收之后,已释放的物理存储器就可以供系统中的所有其他进程使用,如果试图访问未回收的内存,将会造成访问违规。

 

FangSH

2011-01-06

 

posted @ 2011-01-06 21:57  xyecho  阅读(639)  评论(0编辑  收藏  举报