现在的位置: 首页 > 综合 > 正文

WinCE内存管理

2013年03月07日 ⁄ 综合 ⁄ 共 12196字 ⁄ 字号 评论关闭

内存管理

      
如果你在写Windows CE
程序中遇到的最重要的问题,那一定是内存问题。一个WinCE 系统可能只有4MB
RAM,这相对于个人电脑来说是十分少的,因为个人电脑的标准配置已经到了128MB
甚至更多。事实上,运行WinCE 的机器的内存十分缺乏,以至于有时候有必要在写程序的时候为节约内存而牺牲程序的整体性能。

      
幸运的是,尽管WinCE系统的内存很小,但可用来管理内存的函数却十分完善。WinCE实现了Microsoft Windows XPMicrosoft Windows Me中可用到的几乎全部的Win32内存管理APIWinCE支持虚拟内存(virtual
memory
)分配,本地(local)和分离(separate)的堆(heaps),甚至还有(memory-mapped files)内存映射文件。

      
Windows XP一样,WinCE支持一个带有应用程序间内存保护功能的32位平面地址空间,但是WinCE是被设计来应用于不同场合,所以它底层的内存结构不同于Windows
XP
。这些不同能够影响到你如何设计一个WinCE 应用程序。在这一章中,我将讲述最基础的WinCE内存结构。我也将讲述包括WinCE中可用的内存分配方式中的不同点以及如何使用这些不同的内存类型来最小化你的程序的内存占有率。

内存基础

      
对所有的电脑来说,系统地运行一个WinCE,需要ROM(只读存储器)和RAM(随机存储器)。但不论如何,在WinCE系统中,ROMRAM的使用还是稍微有些不同于个人电脑环境。

关于RAM

       RAMWinCE
系统中被分为两个区域:第一个是程序的存储区(program memory),也叫做系统堆(system heap)。第二个是对象存储区(object store)。这个对象存储区有点像一个永久的虚拟RAM磁盘。不同于PC上的旧式的虚拟RAM磁盘,对象存储区保留存储的文件甚至当系统被关闭以后。(脚注)这种安排的原因是WinCE
系统,例如Pocket PC代表性地具有一个主电池和一个备用电池。当用户更换主电池的时候,备用电池的工作是提供电源给RAM以便维持文件在对象存储区的存储。当用户按了重启键之后,WinCE核心就开始寻找在关闭系统前建立的对象存储区,如果找到的话就将继续使用它。

       RAM中的另一个区域则用作程序存储区。程序存储区有点像个人电脑中的RAM,它为正在运行的应用程序保存堆和栈的内容。在对象存储区和程序存储区之间的分界线是可以通过移动它来改变的,用户可以在控制面板中找到改变这条分界线的设置。在可用内存降低的(low-memory)条件下,系统将会弹出对话框询问用户是否要将对象存储区RAM划分一些给程序存储区RAM以满足要运行的应用程序的需求。

关于ROM

      
在个人电脑中,ROM是用来存储BIOS(基本输入输出系统)并且只有64128KB。在WinCE系统中,ROM大小可以从4MB32MB并且存放整个操作系统以及和系统捆绑在一起的应用程序。在这种情况下,ROMWinCE系统中就好像一个只读的硬盘。

      
在一个WinCE系统中,存储在ROM之上的程序能够以现场执行(Execute in PlaceXIP)的方式运行。换句话说,程序可以直接从ROM中执行而不必先加载到RAM中再执行。这种能力对小型系统来说,使之在两个方面具有巨大的优势。代码直接从ROM中执行意味着程序代码不会占据更有价值的RAM。同样,程序在执行前也不必先复制到RAM中,这样就只需要很少的时间来启动一个应用程序。不在ROM中,但是被包含在对象存储区(译者注:上文将对象存储区比作永久的RAM磁盘,故此处要说明,只有Intel力推的nor
flash memroy
类型才能以XIP方式执行,ROM其实也是一种nor flash memory类型)或闪存卡(Flash memory storage card)中的程序将不能以现场方式执行,它们将被复制到RAM中再执行。

关于虚拟内存

       WinCE
实现了系统的虚拟内存管理,在一个虚拟内存系统中,应用程序主要处理这个分离(译者注:物理上可能分离,但系统将它们联系起来),虚拟的地址空间,因此并不涉及到由硬件管理的物理内存。操作系统使用微处理器的内存管理单元来处理虚拟地址和物理地址间的实时转换。

      
这种虚拟内存方法的优势能从MS-DOS系统复杂的地址空间看出来。一旦请求的RAM超过最初PC设计的640-KB限制,程序设计者将不得不作出像扩展内存一样的计划以便增加可用内存的数量。OS/2
1.x
(译者注:IBM研制的操作系统)和Windows 3.0采用了一种基于段(segment-based)的虚拟内存系统来解决问题。应用程序使用虚拟内存不需要知道实际物理内存的位置,只要有内存可用就行。在这些系统中,虚拟内存以一种段的方式被实现了,即可移动的内存块(译者注:段其实就是内存分块)大小从16字节到64KB64-KB的限制并不是由于段本身原因,而是由于Intel
80286
的特性所致,这就是Windows3.xOS/21.x的分段式虚拟内存系统结构。

分页存储

Intel 80386支持的段大小已经超过64KB,但是MicrosoftIBM开始设计OS/2
2.0
,他们选择了一种不同的虚拟内存系统,随后也被386所支持,这就是分页式虚拟内存系统。在一个分页存储的系统中,最小的可被微处理器管理的单元是页(page)。对于Windows NTOS/2 2.0系统来说,页大小都被设置为386处理器默认的4096字节。当一个应用程序存取一个页的时候,微处理器将转换该页的虚拟内存地址到实际的ROMRAM中的物理页(译者注:这就是实现了地址映射和转换,将虚拟的和实际的存储单元一一对应),这一页同时被标记以便其他程序对该页的访问将被排斥。操作系统决定虚拟内存页是否有效,如果有效,将做一个物理内存页到虚拟页的映射。

WinCE实现了一个和其他Win32操作系统类似的分页式虚拟内存系统。在WinCE中,一页的大小可以从1024字节到4096字节,基于微处理器的不同而不同。这和Windows
XP
不同,Windows XP页面尺寸是Intel微处理器所支持的4096字节。对WinCE所支持的CPU类型来说,有486IntelStrong-ARM,和Hitachi
SH4
都是是用了4096-byte 的页面。NEC 4100Windows CE 3.0中使用了4-KB的页面尺寸但是在较早期的开放式系统版本中使用了1-KB的页面大小。

虚拟内存页可以处在三种状态:自由(free),保留(reserved),或被提交(committed),)。自由页就像它的名称一样,自由并且可被分配。保留页是虚拟地址已经被保留,并且不能被操作系统或进程中的其他线程重新分配。保留页不能用在别处,但是它同样不能被当前程序使用,因为它没有被映射到物理内存。要想执行映射,它必须被提交,一个提交页能被应用程序保留,并且直接映射到物理地址。

所有我刚才讲述的内容对有经验的Win32
程序员们来说是些陈旧的知识。对Windows CE 程序员来说最重要的东西是学习Windows CE
是如何改变这些因素的。当Windows CE 实现了大部分和它的老大哥Win32一样的内存API集的时候,Windows CE下面的基础结构将影响到上面的程序。在分开来看Window CE
应用程序的内存结构之前,让我们先来看看一些提供系统内存全局状态的函数。

查询系统的内存

      
如果一个应用程序知道系统当前的内存状态,它将可以较好地管理可用到的资源。WinCE实现了Win32GetSystemInfoGlobalMemoryStatus函数,GetSystemInfo函数原型如下:

VOID GetSystemInfo (LPSYSTEM_INFO lpSystemInfo);

它传递了一个指针给SYSTEM_INFO结构,定义如下

typedef struct {

    WORD wProcessorArchitecture;

    WORD wReserved;

    DWORD  dwPageSize;

    LPVOID lpMinimumApplicationAddress;

    LPVOID lpMaximumApplicationAddress;

    DWORD  dwActiveProcessorMask;

    DWORD  dwNumberOfProcessors;

    DWORD  dwProcessorType;

    DWORD  dwAllocationGranularity;

    WORD  wProcessorLevel; 

    WORD  wProcessorRevision;

} SYSTEM_INFO;

    wProcessorArchitecture参数表示系统微处理器的架构。它的值是定义在Winnt.h中,例如PROCESSOR_ARCHITECTURE_INTELWindows
CE
扩展了这些常数,包括PROCESSOR_ARCHITECTURE_ARMPROCESSOR_ARCHITECTURE_SHx,等等。增加的常数包括像Win32操作系统支持的网络CPUnet CPU)。跳过一些参数,我们看dwProcessorType参数,它来自于特定的微处理器类型。常数有Hitachi
SHx
架构中的PROCESSOR_HITACHI_SH3PROCESSOR_HITACHI_SH4。最后两个参数,wProcessorLevelwProcessorRevision,指明了CPU类型的特征。wProcessorLevel参数类似于dwProcessorType参数,它一个指定的微处理器系列中被定义了,dwProcessorRevision告诉你模式(model)和芯片的步进级别(stepping
level
)。

       dwPageSize参数说明了微处理器页面的大小,以字节为单位。知道这个值,将会在你直接处理虚拟内存API的时候带来方便,在此我只作简短说明。lpMinimumApplication­AddresslpMaximumApplicationAddress参数说明了应用程序可用到的最小和最大的虚拟内存地址。dwActiveProcessorMaskdwNumberOfProcessors参数显示被Window
XP
系统支持的多个处理器数量。因为Windows CE 只支持一个处理器,所以你可以忽略这个参数。dwAllocationGranularity参数说明了一个完整的虚拟内存区域分配的界限。像Windows XPWindows CE
规定虚拟区为64-KB的界限(译者注:作者此处64-KB的意思是即使你只分配一个字节的内存,系统也将会保留64-KB的虚拟地址空间给它,这个值一般是由硬件代码实现的,但是不同硬件可能不同值)。

      
第二个方便的检测系统状态的函数如下:

void GlobalMemoryStatus(LPMEMORYSTATUS lpmst);

它返回一个MEMORYSTATUS结构,定义为

typedef struct { 

    DWORD dwLength; 

    DWORD dwMemoryLoad; 

    DWORD dwTotalPhys; 

    DWORD dwAvailPhys; 

    DWORD dwTotalPageFile; 

    DWORD dwAvailPageFile; 

    DWORD dwTotalVirtual; 

    DWORD dwAvailVirtual; 

} MEMORYSTATUS;

    dwLength参数在调用这个函数之前必须初始化。dwMemoryLoad参数是一个不确定的值;这是一个可用的一般性的参数指示了当前系统的内存使用情况(译者注:该参数是一个近似的百分比值,指明了物理内存的使用情况)。dwTotalPhysdwAvailPhys参数指明了RAM有多少页被分配给了程序存储区RAM,和还有多少可用(译者注:实际上是以字节为单位)。这些值不包括分配对象存储区的RAM

       dwTotalPageFiledwAvailPageFile参数在Windows
XP
下和Windows Me下指明了当前页面文件(paging file)的状态。因为Windows CE不支持页面文件,所以这些参数总是0dwTotalVirtualdwAvailVirtual参数指明了总共的和可用的可被应用程序存取的虚拟内存页的数量(译者注:参数都是以字节为单位,而不是指页数,dwAvailVirtual是指未保留和未提交的内存)。

      
通过GlobalMemoryStatus返回的信息可以验证Windows CE内存结构,通过在有32MBRAMHP iPaq Pocket PC上调用函数,返回值如下:

dwMemoryLoad       0x18          (24)

dwTotalPhys        0x011ac000    (18,530,304)

dwAvailPhys        0x00B66000    (11,952,128)

dwTotalPageFile    0

dwAvailPageFile    0

dwTotalVirtual     0x02000000    (33,554,432)

dwAvailVirtual     0x01e10000    (31,522,816)

 

    dwTotalPhys参数表明了系统的32MB RAM,分配了18.5MB给程序存储区RAM,其中12MB仍然可用。注意这对应用程序来说,并不是通过这次调用,就知道了另外14MBRAM是分配给对象存储区的。要检测分配给对象存储区的RAM的大小,要使用GetStoreInformation

       dwTotalPageFiledwAvailPageFile参数是0,表明页面文件不被Windows
CE
所支持。dwTotalVirtual参数十分有趣,因为它显示了Windows CE
强制给程序的32-MB的虚拟内存限制。其间,dwAvailVirtual参数显示了只使用了32MB虚拟内存的一小部分(译者注:即33,554,432-31,522,816=2,031,616)。

应用程序的地址空间

尽管和Windows XP的应用程序设计类似,但Windows CE应用程序地址空间有一个巨大的不同影响到应用程序。在Windows CE之下,一个应用程序被限制在虚拟内存空间中它自己的32MB slot(槽)和
32MB
slot 1中,slot 1用来加载基于XIPDLL(译者注:Windows CE将虚拟地址空间分为33slot,每个slot
32MB
,序号从0-32 ,附插图c-1,c-2)。当系统只有4MB RAM的时候,分配给应用程序32MB的虚拟地址空间看起来是比较合理的,Win32的程序员在使用这个2-GB的虚拟地址空间的时候,必须记住对Windows
CE
应用程序的虚拟地址空间限制。

7-1展示了一个应用程序的64-MB虚拟地址空间,其中包括32MB用于XIPDLL空间。

7-1     Windows CE的内存映射图

 

      
要注意的是应用程序是以一个64-KB的内存区域开始从0x10000映射,记得最低的64KB地址空间是由Windows为所有应用程序保留的。文件映象包括代码,静态数据段和资源段。在实际过程中,当应用程序启动时代码页(code
pages
)不会载入进来,只有在需要该页面被载入的时候,代码才被载入进来。

      
只读静态数据段(read-only static data segment)和可读写静态数据区(read/write static data areas)只占很少的页面。这些段都是排列在一起的。如同代码一样,只有当这些数据段被应用程序读或者写的时候才会提交给RAM。应用程序的资源将被载入到一些分离的页面中,这些资源是只读的,并且只有当它们被应用程序获取的时候才会分页进入RAM

      
应用程序的栈(stack)被映射到资源段之上。栈的段位置很容易被找到,因为它提交的页就在被保留的区域的尾部。栈的表现是从高地址到低地址增长(译者注:即从高到低填满地址)。如果该应用程序有超过一个线程,那么应用程序的地址空间就会保留超过一个的栈的段。

      
紧接着栈的就是本地堆(local heap)。引导程序保留了大量的页,大约有几百K交给heap来使用,但是只提交满足mallocnewLocalAlloc函数调用分配的内存(译者注:这里是说,被分配多少内存才可以提交多少内存,没被分配的不能用作提交)。从本地堆的保留页尾部到non-XIP
DLL
开始的部分剩余保留页面将被映射为自由的保留空间,如果RAM允许,应用程序可以提交这些保留页。Non-XIP DLLs
就是不能被在ROM中现场执行的DLL将被从上至下载入到32MB的地址空间。Non-XIP DLLs
包括那些被压缩存储在ROM中的DLL。被压缩的ROM
中的文件在被载入到RAM中执行前必须先解压缩。

      
被保留给XIP DLLs32MB
应用程序地址空间的较高位置。Windows CE 映射这些XIP DLLs的代码进入这个空间(译者注:即较高空间),而可读写段被映射进较低位置。从Windows CE 4.2开始,在ROM中的纯资源的DLL将被载入到应用程序64MB空间之外的虚拟内存空间。

 

脚注

PocketPC这样的移动式系统中,当用户按下关闭按钮时系统将不会被真正的关闭,系统进入一种低功耗的挂起状态。 

 

内存分配的不同类型

      
一个Windows CE
应用程序有许多不同的内存分配方式。在内存食物链的底端是Virtualxxx 函数,它们直接保留,提交和释放(free)虚拟内存页。接下来的是堆(heap API。堆是系统为应用程序保留的内存区域。堆有两种风味:当应用程序启动时自动默认分配的本地堆(local
heap
),以及能够由程序手动创建的分离堆(separate heap)。在堆API之后是静态数据,数据块是被编译器定义好的或者由程序手动创建的。最后,我们来看栈,这是程序为函数存储变量的区域。

      
一个Windows CE不支持的Win32
内存API是全局堆(global heap)。全局堆API包括Global­AllocGlobalFreeGlobalRealloc,将不会出现在Windows
CE
中(译者注:很奇怪,我在Windows CE 中仍然可以使用这几个API,并且工作正常,好像Microsoft并没有把它们完全去掉)。全局堆只是从Windows 3.xWin16
时期继承而来。在Win32中,全部和本地的堆很类似,全局内存一个独特用法是,为剪贴板的数据分配内存,在Windows CE中已经被本地堆替代并加上了句柄。

      
Windows CE中最小化内存使用的关键是选择与内存块使用模型相匹配的恰当的内存分配策略。我将回顾一下这些内存类型然后讲述Windows CE应用程序中的最小化内存使用策略。

虚拟内存

      
虚拟内存是内存类型中最基础的。系统调用虚拟内存API来为其他类型内存分配内存。包括堆和栈。虚拟内存API,包括VirtualAllocVirtualFreeVirtualReSize函数,这些可以直接操作应用程序虚拟内存空间的虚拟内存页面。页面可以保留,提交给物理内存,或使用这些函数释放。

分配虚拟内存

      
分配和保留虚拟内存是同过这个函数完成的:

LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize,

                     DWORD flAllocationType,

                     DWORD flProtect);

VirtualAlloc的第一个参数是要分配内存区域的地址。当你使用VirtualAlloc来提交一块以前保留的内存块的时候,lpAddress参数可以用来识别以前保留的内存块。如果这个参数是NULL,系统将会决定分配内存区域的位置,并且围绕64-KB的范围(译者注:就是前面说提及的最小内存分配尺寸)。第二个参数是dwSize,要分配或者保留的区域的大小。这个参数以字节为单位,而不是页,系统会根据这个大小一直分配到下页的边界。

flAllocationType参数指定了分配的类型,你可以指定或者合并以下标志:MEM_COMMITMEM_AUTO_COMMITMEM_RESERVEMEM_TOP_DOWNMEM_COMMIT标志分配程序使用的内存,MEM_RESERVE保留虚拟地址空间以便以后提交。保留的页不能存取直到调用VirtualAlloc的时候再次指定了MEM_COMMIT标志。第三个标志,MEM_TOP_DOWN,告诉系统从最高可允许的虚拟地址开始映射应用程序。

The MEM_AUTO_COMMIT标志是唯一一个Windows CE最方便的标志,当这个参数被指定了之后,内存块立即被保留,当其中的页被第一次存取的时候,系统将自动提交该页。这允许你分配大块的虚拟内存而不需要顾及系统和实际RAM分配直到当前页被第一次使用。自动提交内存的缺点是,物理RAM需要退回当页面被第一次访问时可能不可用的页面。在这种情形下,系统将产生一个异常(exception)(译者注:可能会出现因为无法访问而出错)。

       VirtualAlloc可以通过并行多次调用提交一个区域的部分或全部来保留一个大的内存区域。多重调用提交同一块区域不会引起失败。这使得一个应用程序保留内存后可以随意提交将被写的页。当这种方式不在有效的时候,它会释放应用程序通过检测被保留页的状态看它是否在提交调用之前已经被提交。

       flProtect参数指定了被分配区域的访问保护方式。这些不同的标志被总结在下面的列表中:

PAGE_READONLY

该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访问。

PAGE_READWRITE

区域可被应用程序读写。

PAGE_EXECUTE

区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。

PAGE_EXECUTE_READ

区域包含可执行代码,应用程序可以读该区域。

PAGE_EXECUTE_READWRITE

区域包含可执行代码,应用程序可以读写该区域。

PAGE_GUARD

区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限。

PAGE_NOACCESS

任何访问该区域的操作将被拒绝。

PAGE_NOCACHE

RAM中的页映射到该区域时将不会被微处理器缓存(cached)。

PAGE_GUARDPAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页(guard
page
),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。

区域和页

      
在我继续谈论虚拟内存API之前,我需要说明一个比较细微的差异。虚拟内存在区域内被保留是以64KB为基础的。在区域内的页面能够一页一页地被提交(译者注:前面说到在Windows CE中每页是4096字节或1024字节)。你可以直接提交一页或者几页而不是保留区域的全部页。但是对页或几页来说,直接提交的仍是以64-KB为单位(译者注:可以直到被提交的页数量足够填满64KB才真正提交),因为这个原因,最好保留一块64-KB的虚拟内存,然后提交那些需要的页到区域里。

      
因为对每个进程32MB虚拟内存地址空间的限制,这就有了一个最大值 32MB/64KB-1=511,这是虚拟内存在内存溢出前能被保留的最大值。接下来,有个例子,代码段如下:

#define PAGESIZE 1024   // Assume we're on a 1-KB page machine

for (i = 0; i < 512; i++) 

pMem[i] = VirtualAlloc (NULL, PAGESIZE, MEM_RESERVE  MEM_COMMIT,PAGE_READWRITE);

代码分配512个单页的虚拟内存。甚至你系统还有一半的可用RAMVirtualAlloc也会在完成分配前失败。因为它的运行已经超出了应用程序的虚拟地址空间。发生这种情况是因为每1-KB的块要占用64-KB的空间,接下来应用程序的代码,栈,和本地堆也要映射到同样的32-MB虚拟地址空间,可用的虚拟分配区域通常不超过475个。

    
一个比较好的分配512块特殊内存的方法是这样做:

#define PAGESIZE 1024   // Assume we're on a 1-KB page machine.

// Reserve a region first.

pMemBase = VirtualAlloc (NULL, PAGESIZE * 512, MEM_RESERVE,

                         PAGE_NOACCESS);

 

for (i = 0; i < 512; i++) 

    pMem[i] = VirtualAlloc (pMemBase + (i*PAGESIZE), PAGESIZE, 

                            MEM_COMMIT, PAGE_READWRITE);

    
代码首先保留了一块区域,页面将在以后被提交。因为区域已经被先保留了,提交页就不受64-KB限制(译者注:只有保留页最小值受64KB限制),等等,如果你系统中有512KB的可用内存,分配将会成功。

    
尽管我刚才给你看的是一个人为的例子(还有比直接分配虚拟内存更好的方法来分配1-KB的内存块),这中内存分配方法验证了一个重要的不同(对于其他Windows系统)。在桌面版本的Windows中,工作中的应用程序有一个完全的2-GB的虚拟地址空间。在Windows
CE
中,一个程序员必须明白每个应用程序只被保留了较小的32-MB虚拟地址空间。

释放虚拟内存

      
你可以通过调用VirtualFree来取消提交,或释放虚拟内存。从物理RAM页中取消提交或者取消映射,但是保持页被保留的状态。函数原型如下:

    BOOL VirtualFree (LPVOID lpAddress, DWORD dwSize,

                  DWORD dwFreeType);

lpAddress参数是一个指针,指向要被释放或取消提交的虚拟内存的区域。dwSize参数指明要取消提交区域的大小,以字节为单位。如果区域要被释放,这个值必须是0dwFreeType参数包含了操作类型标志,MEM_DECOMMIT标志指定了区域将被取消提交但是仍被保留,MEM_RELEASE标志说明区域要取消提交并且释放。

在区域中的所有的页通过VirtualFree被释放必须处在同样的情况下。更确切地说,区域中的全部页要被释放,那这些页要么都是被提交的页,要么都是被保留的页。如果有些页被提交,有些页被保留,那么VirtualFree函数调用就会失败。

改变和查询权限

      
你可以通过调用VirtualProtect来修改最初通过VirtualAlloc指定的虚拟内存区域的访问权限。这个函数只能改变被提交的页的访问权限。函数的原型如下:

BOOL VirtualProtect (LPVOID lpAddress, DWORD dwSize, 

                     DWORD flNewProtect, PDWORD lpflOldProtect);

开始的两个参数lpAddressdwSize,指定了函数作用的块的大小。flNewProtect参数包含区域的新的保护标志。这些标志和我前面提到的VirtualAlloc函数使用的一样。lpflOldProtect参数指向一个DWORD,将返回旧的保护标志(译者注:如果此处为NULL或指向一个无效的变量,函数将会失败)。

当前区域的保护权限可用通过下面的调用查询:

DWORD VirtualQuery (LPCVOID lpAddress, 

                    PMEMORY_BASIC_INFORMATION lpBuffer,

                    DWORD dwLength);

    lpAddress参数包含区域开始查询的地址。lpBuffer指针指向我很快就要提到的一个PMEMORY_BASIC_INFORMATION结构。第三个参数dwLength,必须包含PMEMORY_BASIC_INFORMATION结构的大小。

       PMEMORY_BASIC_INFORMATION结构被定义如下:

typedef struct _MEMORY_BASIC_INFORMATION { 

    PVOID BaseAddress; 

    PVOID AllocationBase; 

抱歉!评论已关闭.