分类: 工作 |
内存分配的不同类型
虚拟内存
分配虚拟内存
LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize,
VirtualAlloc
的第一个参数是要分配内存区域的地址。当你使用VirtualAlloc
来提交一块以前保留的内存块的时候,lpAddress
参数可以用来识别以前保留的内存块。如果这个参数是NULL
,系统将会决定分配内存区域的位置,并且围绕64-KB
的范围(译者注:就是前面说提及的最小内存分配尺寸)。第二个参数是dwSize
,要分配或者保留的区域的大小。这个参数以字节为单位,而不是页,系统会根据这个大小一直分配到下页的边界。
flAllocationType
参数指定了分配的类型,你可以指定或者合并以下标志:MEM_COMMIT
,MEM_AUTO_COMMIT
,MEM_RESERVE
和MEM_TOP_DOWN
。MEM_COMMIT
标志分配程序使用的内存,MEM_RESERVE
保留虚拟地址空间以便以后提交。保留的页不能存取直到调用VirtualAlloc
的时候再次指定了MEM_COMMIT
标志。第三个标志,MEM_TOP_DOWN
,告诉系统从最高可允许的虚拟地址开始映射应用程序。
The
MEM_AUTO_COMMIT
标志是唯一一个Windows CE
最方便的标志,当这个参数被指定了之后,内存块立即被保留,当其中的页被第一次存取的时候,系统将自动提交该页。这允许你分配大块的虚拟内存而不需要顾及系统和实际RAM
分配直到当前页被第一次使用。自动提交内存的缺点是,物理RAM
需要退回当页面被第一次访问时可能不可用的页面。在这种情形下,系统将产生一个异常(exception
)(译者注:可能会出现因为无法访问而出错)。
PAGE_READONLY
该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访问。
PAGE_READWRITE
区域可被应用程序读写。
PAGE_EXECUTE
区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。
PAGE_EXECUTE_READ
区域包含可执行代码,应用程序可以读该区域。
PAGE_EXECUTE_READWRITE
区域包含可执行代码,应用程序可以读写该区域。
PAGE_GUARD
区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限。
PAGE_NOACCESS
任何访问该区域的操作将被拒绝。
PAGE_NOCACHE
RAM
中的页映射到该区域时将不会被微处理器缓存(cached
)。
PAGE_GUARD
和PAGE_NOCHACHE
标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD
标志指定了一个防护页(guard page
),即当一个页被提交时会因第一次被访问而产生一个one-shot
异常,接着取得指定的访问权限。PAGE_NOCACHE
防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA
)来共享内存块。
区域和页
#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个单页的虚拟内存。甚至你系统还有一半的可用RAM,VirtualAlloc也会在完成分配前失败。因为它的运行已经超出了应用程序的虚拟地址空间。发生这种情况是因为每1-KB的块要占用64-KB的空间,接下来应用程序的代码,栈,和本地堆也要映射到同样的32-MB虚拟地址空间,可用的虚拟分配区域通常不超过475个。
#define PAGESIZE 1024 // Assume we're on a 1-KB page machine.
// Reserve a region first.
pMemBase = VirtualAlloc (NULL, PAGESIZE * 512, MEM_RESERVE,
for (i = 0; i < 512; i++)
释放虚拟内存
BOOL VirtualFree (LPVOID lpAddress, DWORD dwSize,
lpAddress
参数是一个指针,指向要被释放或取消提交的虚拟内存的区域。dwSize
参数指明要取消提交区域的大小,以字节为单位。如果区域要被释放,这个值必须是0
,dwFreeType
参数包含了操作类型标志,MEM_DECOMMIT
标志指定了区域将被取消提交但是仍被保留,MEM_RELEASE
标志说明区域要取消提交并且释放。
在区域中的所有的页通过VirtualFree
被释放必须处在同样的情况下。更确切地说,区域中的全部页要被释放,那这些页要么都是被提交的页,要么都是被保留的页。如果有些页被提交,有些页被保留,那么VirtualFree
函数调用就会失败。
改变和查询权限
BOOL VirtualProtect (LPVOID lpAddress, DWORD dwSize,
开始的两个参数
lpAddress和
dwSize
,指定了函数作用的块的大小。flNewProtect
参数包含区域的新的保护标志。这些标志和我前面提到的VirtualAlloc
函数使用的一样。lpflOldProtect
参数指向一个DWORD
,将返回旧的保护标志(译者注:如果此处为NULL
或指向一个无效的变量,函数将会失败)。
当前区域的保护权限可用通过下面的调用查询:
DWORD VirtualQuery (LPCVOID lpAddress,
typedef struct _MEMORY_BASIC_INFORMATION {
} MEMORY_BASIC_INFORMATION;
MEMORY_BASIC_INFORMATION
结构的第一个字段是BaseAddress
,是传递给VirtualQuery
函数的一个地址。AllocationBase
字段包含使用VirtualAlloc
函数分配的区域的基地址,AllocationProtect
字段包含区域原来被分配时的保护属性。RegionSize
字段包含从传递给VirtualQuery
的指针开始到一系列具有相同属性的页为结尾的区域大小(译者注:这里是从基地址开始)。State
字段包含区域中页的状态-自由,保留,提交。Protect
字段可以包含MEM_PRIVATE
标志,指明该区域包含应用程序私有的数据;MEM_MAPPED
指明该区域被映射为一个内存映射文件;MEM_IMAGE
指明该区域被映射为一个EXE
或DLL
模块。
理解VirtualQuery
最好的方式是看例子,比方说一个应用程序保留了16,384字节(在以页面大小为1-KB的机器中占16页)。系统从地址0xA0000开始保留这16-KB的块。后来应用程序从最初的区域中提交了从第2048字节(2页)开始的9216字节(9页)。图7-2显示了这个假设的情况。
图7-2被保留的区域有9页被提交
BaseAddress 0xA1000
AllocationBase 0xA0000
AllocationProtect PAGE_NOACCESS
RegionSize 0x1C00 (7,168 bytes or 7 pages)
State MEM_COMMIT
Protect PAGE_READWRITE
Type MEM_PRIVATE
BaseAddress
字段包含传递给VirtualQuery
的地址,值为0xA1000,在最初的区域中是第4096字节。
AllocationBase
字段包含最初区域的地址。当AllocationProtect
设为PAGE_NOACCESS
时,指明区域是最初被保留的,而不是直接提交。RegionSize
字段包含传递给VirtualQuery
的指针0xA1000开始,到被提交的页结束地址0xA2C00的字节数。
State和
Protect
字段包含的标志表明当前的页状态。Type
字节表明区域被应用程序分配给自己使用。