一、RAM和ROM 尽管Windows CE系统主要用在嵌入式系统上,且需要更加高效、节省地使用物理内存,但在Windows XP和Windows Me中采用的内存管理API函数被几乎完整的保留下来。 Windows CE设备一般没有磁盘驱动器,它的内存通常是由RAM和ROM组成的。 Windows CE中的RAM分为程序区(也叫系统堆)和对象存储两个区域。其中的对象存储区域可以被看做一个{yj}的虚拟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卡中,断电充电后可通过这个程序xx恢复过来。另外,Pocket PC中的RAM可以动态地调整。 二、虚存 要高性能则需要增加物理内存,而增加物理内存则增加成本。为在成本和性能之间找到一个较好的平衡点,虚存的技术应运而生。 不同的体系结构,其各自的内存结构也存在一定的差异。如:SHX系列和MIPS系列的CPU,起物理地址的映射是由CPU来完成的,内核可以直接访问512MB的物理内存;而对X86和ARM系列的CPU来说,在启动的过程中,内核会将现有的物理地址全部映射到0x8000 0000以上的虚拟地址空间中。 Windows CE与Windows XP的共同点:1、都支持4GB大小的虚拟地址空间,上面的2GB归系统使用,下面的2GB归应用程序使用。2、保留{zd1}的64KB的地址空间,任何进程都不可以访问。 Windows CE与Windows XP的不同点:主要体现在低的2GB空间分配上。Windows CE中每个应用程序的内存空间都是受保护的;从{zd1}的虚拟地址空间开始,系统把这一部分分成了33个槽,每个槽的大小为32MB;0号槽存放的是当前xx的进程,通过微处理的页转换表来实现。 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为原来虚拟地址空间中{dy}个页面的保护标志。 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表示创建的堆的空间的{zd0}值,设置为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); 四、栈 栈也是一段连续的虚拟地址空间,但和堆相比,栈的空间要相对小一些,创建的栈的默认{zd0}值为58KB; 栈是专门为函数使用而设计的; 在程序设计时,要尽量避免分配很多、很大的内存块,这样在发现没有足够可用的物理RAM时,线程挂起,一个给定时间无响应后出现系统异常。{zh0}是在堆中分配大的内存块,并且一定要在使用以后把堆释放掉,或者也可以通过在创建线程时指定栈的大小来避免这个问题。 五、静态数据块 Windows CE中的静态数据块是一个程序启动时候加载的内存块。这些内存块包含了字符集、缓冲区和程序需要的静态数据。 Windows CE中,系统会为每一个应用程序申请2个RAM块,一个存放读/写数据,一个存放只读数据。 如:我们在编写一个基于ROM的程序的时候,尽量把数据放在只读静态数据区中。
1. Windows CE支持虚拟内存动态分配(virtual memory allocation),局部和单独的堆空间(Local and separate heaps),甚至内存映射文件(memory_mapped files,memory mapping simplifies file access. Instead of using a system-maintained pointer to write to the file, you can write directly to memory.) 2. Windows CE环境下ROM和RAM的使用和我们平时的PC机有所不同。RAM被分为 两个区域:程序区(proram memory),也叫系统堆(system heap),和对象存储(object store)。 l 对象存储可以被看作类似于一个{yj}的虚拟RAM磁盘。在Pocket PC上,当我们关闭显示屏后,系统实际上并没有真正断电,而是进入一个低功耗的睡眠状态(由主电池来供电)。当我们按下后面的Reset按钮后,Windows CE内核在RAM中寻找最近一次创建的对象存储,如果找到的话,就是用这个对象存储重新启动。 l RAM的另一个区域是程序区。这个区域就像PC机的RAM一样。它存放所运行程序的堆和栈的空间。对象存储和程序区的界限是可移动的。在低内存的情况下,系统会请求用户把部分可用的存储对象空间提供给运行程序使用,以满足程序运行的RAM需要。 u PC机上,ROM用来存储BIOS,通常是64-128K。在WindowCE系统中,ROM用来存储整个操作系统以及和OS绑定的应用程序,大小可以4-32M不等。因此,WinCE下的ROM就像一个很小的只读硬盘一样。 u 在WinCE OS中,ROM_based程序可以被指定位就地执行(Excute in Place)。也就是说,它们直接从ROM中执行而不是先载入RAM然后再执行。这样不仅可以节省宝贵的Ram资源,而且程序启动更快。不在ROM中的程序(在对象存储空间里的或者是在Flash内存中的)不能原地执行。 3. 虚拟内存。OS使用微处理的内存管理单元实时地把虚拟地址转换到物理地址。 u 分页内存(Paged Memory)。在WinCE OS下,一页是1K或者4K,取决于微处理芯片。Intel 的ARM,一页是4K。 u 虚拟页面有三种状态:未用的(free),保留的(reserved)和占用的(committed)。一个free的页面,顾名思义就是可以被分配的。一个reserved页面就是已经被预定的,所以他的虚拟地址空间不能再分配给操作系统和这个进程的另一个线程。它的地址还未映射到物理空间。一个committed页就是已经被一个程序保留了并且已经直接映射到了一个物理地址空间。 4.WinCE地址空间 u 为所有的应用程序实现单个的2GB的虚拟地址,但是每个应用程序的内存空间都是受保护的,以保证其他应用程序不能访问。从{zd1}的虚拟地址空间开始分为33个slot,每个slot32M。每个slot被分配为当前运行的进程。Slot 0中存放的当前xx的进程。当winCE在进程间切换时,它就重新映射地址空间,把老进程移出,新进程移进slot 0。这个任务是通过操作微处理器的页转换表来迅速完成的。 u 33个slot以上地址区域是为OS和映射内存映射文件保留的。和windows XP一样,winCE也保留了{zd1}的64K地址空间,任何进程都不能访问。 5.查询系统内存。 u GetSystemInfo(LPSYSTEM_INFO) u GlobalMemoryStatus(LPMEMORYSTATUS)(paging files are not supported under windows CE) 6.各种内存分配方式 u 首先是Virtualxxxx函数,用来预留,提交和释放虚拟内存页。然后是堆API。堆使用该应用程序管理的保留内存空间区域。堆有两种:缺省的局部堆和独立堆。堆API下来是静态数据,这些数据块由编译器定义并且在载入时自动分配。{zh1}是栈,它存放应用程序中每个函数的局部变量。 u wince不支持全局堆。因此全局堆API,wince都不支持。 u 在wince中减少内存使用的关键是选择恰当的内存分配策略。 6.1 虚拟内存 它是内存类型中最基本的。系统可以调用虚拟内存API来为其他内存类型分配空间,比如堆,栈。VirtualAlloc,VirtualFree,VirtualResize函数直接在这个应用程序的虚拟内存空间里操作虚拟内存页。可以使用这些函数预留,提交和是否物理内存。(以页为单位的,舍入到页的界限) 6.2 堆 显然,以页为单位分配内存对于大多数程序是低效的。而堆就可以以字节为单位来申请和释放内存。粒度比虚拟内存函数要小(至少4字节)。系统会随着所运行程序的需求自动增长堆的大小。当堆中的一些块被释放后,系统检查看是否整个页都被释放了,如果是的话,这个页就被decommit。由于在一个堆上不断地allocate和free,这个堆很可能被分割为一些碎片,或者有可能这个堆几乎是空的,但是系统还是不能从这个堆中释放 a page,除非它全空。 u Local Heap。每个应用程序在启动时都会创建一个缺省的堆。可以使用LocalAlloc,LocalFree和LocalRealloc函数操作这个堆。 u Separate Heaps。为了避免把堆分割成很多碎片,如果在一段时间里需要一块连续的块空间的话,更好的方法是创建一个单独的堆。实际申请空间时才映射物理地址空间,并不才创建时就保留或者提交任何内存。Destroy这个堆时并不要求里面的数据块都释放了。 6.3栈 缺省一个栈空间{zd0}是58K。当栈空间逐渐变小时,系统将进入低内存状态。栈空间的{zd0}值可以在链接时指定。可以指定到1M。注意: u 缺省栈空间大小也是所有其他独立线程栈的大小。 u 如果没有可用的物理RAM,需要栈空间的线程就会被挂起。如果在给定的一小段时间内,这个内存需求不能得到响应,就会弹出系统异常。在低内存情况下不应该尝试使用大量的栈空间。 6.4 静态数据 预定义内存块在程序载入时为它们自动分配空间。这些块中的数据包含了静态申请的字符串,缓冲区,和全局变量以及和这个应用程序静态链接的函数库。Wince为一个应用程序申请两个RAM块:一个用于存放Read/Write数据,一个用于存放只读数据。由于这些区域都是以页为单位来分配内存的,所以为了节省空间,尽量使静态数据段接近但不能超过页的范围。如果在静态数据段还有相当空间,可以把本来动态申请的一些缓冲区移到静态数据区。有时根据具体情况可以把只读数据区中的一些数据移到读写数据区。决定的方法是查看map文件。 7.在低内存情况下WinCE如何处理应用程序的内存请求。 当系统运行需要更多的内存,而又不能满足的情况下,shell会自动关闭该应用程序。因此wince提供了一些方法来在所有正在运行的程序间分配宝贵的内存资源。 u WM_HIBERNATE消息。OS首先向非活动状态的程序发送此消息,请求它们在不破坏各自内在状态情况下尽量释放多的内存,比如释放GDI对象,缓冲数据等等。 u 内存门槛(memory thresholds)。WinCE定义了四种内存状态:normal, limited, low and critical。这些状态的划分主要取决于当前可用的内存大小。当可用内存很少时,系统首先发送WM_HIBERNATE消息,然后限制内存申请的请求。 |