一、RAM和ROM
尽管Windows CE系统主要用在嵌入式系统上,且需要更加高效、节省地使用物理内存,但在Windows XP和Windows Me中采用的内存管理API函数被几乎完整的保留下来。
Windows CE设备一般没有磁盘驱动器,它的内存通常是由RAM和ROM组成的。
Windows CE中的RAM分为程序区(也叫系统堆)和对象存储两个区域。其中的对象存储区域可以被看做一个永久的虚拟RAM盘。和传统的PC机不同,Windows CE中的对象存储在系统关闭以后仍旧可以保存文件。因为在我们关闭电源的时候,系统并没有真正关闭,而是进入一个低功耗的运行模式。RAM的另一个区域就是程序区,这个区域就跟PC机的RAM一样。
Windows CE系统被设计为主电池和备份电池的双电池策略。
ROM称为“Read Only Memory”。Windows CE中,存储在ROM中的程序可以被设计成本地执行(XIP:Execute in place)。该方式可直接在ROM中运行,不像一般的程序运行方式,即无须释放到RAM中再执行。但XIP方式中,ROM的CPU访问速度较慢,实时性较差。
需要注意的是,存储在闪存(Flash Memory)中和对象存储区(Object Store)的程序不能被本地执行,必须拷到RAM中,然后再运行。
实际的应用中,如Pocket PC在出厂时已经将操作系统和一些应用软件安装到ROM中,而我们自行安装的软件则通常会在RAM中。为防止RAM中的数据丢失,微软公司在Pocket PC中安装一个叫做“Backup”的程序,它会将安装好的程序备份到内置的SD卡或者CF卡中,断电充电后可通过这个程序完全恢复过来。另外,Pocket PC中的RAM可以动态地调整。
二、虚存
要高性能则需要增加物理内存,而增加物理内存则增加成本。为在成本和性能之间找到一个较好的平衡点,虚存的技术应运而生。
不同的体系结构,其各自的内存结构也存在一定的差异。如:SHX系列和MIPS系列的CPU,起物理地址的映射是由CPU来完成的,内核可以直接访问512MB的物理内存;而对X86和ARM系列的CPU来说,在启动的过程中,内核会将现有的物理地址全部映射到0x8000 0000以上的虚拟地址空间中。
Windows CE与Windows XP的共同点:1、都支持4GB大小的虚拟地址空间,上面的2GB归系统使用,下面的2GB归应用程序使用。2、保留最低的64KB的地址空间,任何进程都不可以访问。
Windows CE与Windows XP的不同点:主要体现在低的2GB空间分配上。Windows CE中每个应用程序的内存空间都是受保护的;从最低的虚拟地址空间开始,系统把这一部分分成了33个槽,每个槽的大小为32MB;0号槽存放的是当前激活的进程,通过微处理的页转换表来实现。
Windows CE采用的是分页式虚拟存储(Page Virtual Memory System),微处理管理的最小内存单元叫做页。当一个应用程序需要访问一个页的时候,微处理器就会根据需要把虚拟地址转换成RAM或ROM中的实际物理地址。根据处理器类型,Windows CE中一个页的大小是1024个字节或者4096个字节。
虚拟内存分为3种状态:1、未使用的(free):未使用的虚拟页面,可以被分配的;2、保留的(reserved):已经被预定了,但还没与实际的物理地址对应,不能被程序使用;3、占用的(committed):已经与实际的物理地址对应了。
Windows CE中,虚拟内存被分成64KB大小的一个个区域,然后在每个区域空间中的页面被按页提交。
有关虚拟内存的一些函数:
1、分配和预订保留虚拟内存
LPVOID VirtualAlloc(LPVOID lpAddress,DWORD dwSize,DWORD flAllocationType,DWORD flProtect)
lpAddress:虚拟地址空间的首地址。若设置为0则内核将自动查找一个符合要求的首地址;
dwSize:为空间的大小;
flAllocationType:分配类型;
flProtect:分配的虚拟地址空间设置保护标志。
2、设置虚拟内存
BOOL VirtualFree(LPVOID lpAddress,DWORD dwSize,DWORD dwFreeType)
lpAddress:虚拟地址空间的首地址;
dwSize:为需要释放的虚拟内存的大小;
dwFreeType:指定的释放类型;
3、更改虚拟内存空间的访问权限
BOOL VirtualProtect(LPVOID lpAddress,DWORD dwSize, DWORD flNewProtect,DWORD lpflOldProtect)
lpAddress:需要重新定义访问权限的虚拟内存的首地址;
dwSize:虚拟内存空间的大小;
flNewProtect:新定义的访问权限;
lpflOldProtect:和参数flNewProtect相对应,lpflOldProtect为原来虚拟地址空间中第一个页面的保护标志。
4、查询一个虚拟地址空间的保护权限
DWORD VirtualQuery(LPVOID lpAddress,PMEMORY_BASIC_INFORMATION lpBuffer, DWORD dwLength)
lpAddress:被查询虚拟地址空间的首地址;
lpBuffer:为指向PMEMORY_BASIC_INFORMATION结构的指针;
dwLength:为PMEMORY_BASIC_INFORMATION结构的大小。
PMEMORY_BASIC_INFORMATION结构定义如下:
Typedef struct _PMEMORY_BASIC_INFORMATION{
PVOID BaseAddress; //查询函VirtualQuery的基地址
PVOID AllocationBase; //用VirtualAlloc函数分配内存时,实际分配的基地址
PVOID AllocationProtect; //分配该页面时页面的一些属性
PVOID RegionSize; //从BaseAddress开始,具有相同属性页面的大小
PVOID State; //页面的状态,有释放,预定,提交三种
PVOID Protect; //当前虚拟地址空间的保护标志
PVOID Type; //内存空间的类型
}PMEMORY_BASIC_INFORMATION
三、堆
从本质上说,堆是一段连续的、相对较大的虚拟地址空间。
使用堆以字节单位来申请和释放内存,这种方式的粒度比分页式虚拟存储要小得多,从而提高应用程序的执行效率
Windows CE中只允许在堆中程序申请静态的(不能移动的)内存块,因此,有可能会产生一系列碎片。
在Windows CE中,每个堆都是由一个信号量来控制访问的,两个进程如果想同时访问一个堆是不允许的。
“粒度”:实际上是数据的细化程度,细化程度越高,粒度越小;细化程度越低,粒度越大。如:调用系统的API函数,调用的频率较高则粒度越小。
任意一个程序在启动的时候,都会创建一个本地堆,通过LocalAlloc、LocalFree和LocalRealloc函数可以对本地堆中的内存块进行分配、释放和调整大小。
缺省情况下,Windows CE系统会保留384个页作为本地堆,但是这些页只有在分配的时候才会提交。如果程序需要在本地堆上分配超过188KB的空间,系统就会为本地堆分配多余的空间。
Windows CE中,分配内存、释放内存和重定义内存大小的函数只是Win32中与本地堆相关函数的一个子集。
1、在本地堆中分配内存
HLOCAL LocalAlloc(UINT uFlags,UINT uBytes);
这个函数返回一个指向本地内存块的句柄;参数uFlags描述的是分配内存块的类型;参数uBytes则表示所分配内存块的大小。
2、释放本地堆中的内存块
HLOCAL LocalFree(HLOCAL hMem);
当分配内存块成功的时候,这个函数将返回一个NULL值。
3、重新设置内存块大小
HLOCAL LocalReAlloc(HLOCAL hMem,UINT uBytes,UINT uFlag);
hMem:由LocalAlloc函数返回的一个指针;uBytes:被重新设置的内存块的大小;uFlags:被重新分配的内存块特性。
4、查询内存块大小
UINT LocalSize(HLOCAL hMem);
为了避免本地堆中出现的碎片现象,当我们在一段时间里需要一段连续的空间,更好的方式是创建一个独立堆。当文件被打开或者被关闭的时候,这些独立堆将被创建或者释放。
1、创建独立堆
HANDLE HeapCreate(DWORD flOptions,DWORD dwInitialSize,DWORD dwMaximumSize);
flOptions可以为NULL后者HEAP_NO_SERIALIZE;dwInitialSize指定的是开始创建堆时提交的物理内存的大小,设置成0表示初始化设置成提交1个页;dwMaximumSize表示创建的堆的空间的最大值,设置为0则表示让Windows来决定保留多少页。
堆的默认大小是188KB;
Windows CE中的HeapCreate函数只是创建了一个堆,而没有给这个堆分配或者保留任何的内存空间。
2、在独立堆中分配内存
LPVOID HeapAlloc(HANDLE hHeap,DWORD dwFlags,DWORD dwBytes);
这个函数返回的类型是指针,而不是句柄,这跟LocalAlloc函数不大一样;
hHeap为制定堆的句柄;dwFlags为标志,可以为HEAP_NO_SERIALIZE和HEAP_ZERO_MEMORY;dwBytes为指定分配的大小,它是以字节为单位的。
3、在独立堆中释放内存
BOOL HeapFree(HANDLE hHeap,DWORD dwFlags,LPVOID lpMem);
hHeap为指定堆的句柄;dwFlags为标志,只允许为HEAP_NO_SERIALIZE;lpMem为指向被释放内内存块的指针。
4、在独立堆中重新分配内存大小
LPVOID HeapReAlloc(HANDLE hHeap,DWORD dwFlags,LOVOID lpMem,DWORD dwBytes);
参数表示:(句柄;标志;内存块指针;内存块大小)
5、在独立堆中查询堆的大小
DWORD HeapSize(HANDLE hHeap,DWORD dwFlags,LOVOID lpMem);
参数表示:(句柄;标志;内存块指针)
6、释放独立堆
BOOL HeapDestroy(HANDLE hHeap);
四、栈
栈也是一段连续的虚拟地址空间,但和堆相比,栈的空间要相对小一些,创建的栈的默认最大值为58KB;
栈是专门为函数使用而设计的;
在程序设计时,要尽量避免分配很多、很大的内存块,这样在发现没有足够可用的物理RAM时,线程挂起,一个给定时间无响应后出现系统异常。最好是在堆中分配大的内存块,并且一定要在使用以后把堆释放掉,或者也可以通过在创建线程时指定栈的大小来避免这个问题。
五、静态数据块
Windows CE中的静态数据块是一个程序启动时候加载的内存块。这些内存块包含了字符集、缓冲区和程序需要的静态数据。
Windows CE中,系统会为每一个应用程序申请2个RAM块,一个存放读/写数据,一个存放只读数据。
如:我们在编写一个基于ROM的程序的时候,尽量把数据放在只读静态数据区中。