张德迪
[摘 要]Windows CE是一种嵌入式实时性的操作系统,在内存管理方面必须要比其它Windows操作系统更节约物理内存和虚拟地址空间。
[关键词]Windows CE 内存管理
[中图分类号]TP311 [文献标识码]A[文章编号]1007-9416(2009)11-0022-01
Windows CE.NET支持32位虚拟内存机制、按需分配内存和内存映射文件,但是与其它Windows操作系统又有明显的不同。毕竟Windows CE是一种嵌入式实时性的操作系统,在内存管理方面必须要比其它Windows操作系统更节约物理内存和虚拟地址空间。
对于早期采用的存储设备一般采用ROM+RAM,在ROM中存放的所有文件可以是压缩的也可以是不压缩的,这取决于OEM(原始设备制造商)。OEM在定制CE内核时可以设置一个标志告诉ROM镜像制作工具(romimage.exe)是否压缩文件。对于ROM中存放的模块(DLL、EXE文件)来说,如果是压缩的,模块在运行前先解压=并全部存放到RAM中。如果是不压缩的,并且ROM介质支持线性访问(line-accessed),就可以本地执行(executed in place,缩写为XIP)。利用本地执行方式运行应用程序、DLL的优点是:采用这种技术在加载EXE或DLL时,其中的代码段数据不加载到物理内存中,内核只是分配虚拟地址空间给代码段,当执行代码时内核会到实际存放在ROM存储设备上的文件中寻找代码并执行。采用这样的技术既可以节省可用内存又可以减少加载的时间。但是这种技术有一定的局限性,如果要让CPU到ROM中去寻找代码执行,那么ROM介质必须支持线性访问,这就要求ROM介质支持线性访问,而不是块访问。XIP这种加载方式的缺点是执行速度相对较慢,CPU访问ROM的速度要慢于访问RAM的速度。
基于Windows CE的产品开始采用FLASH、IDE等永久存储设备时,内核镜像和其它应用程序文件开始存放到永久存储设备中而不是ROM中,这不仅仅是因为硬盘或者FLASH的I/O速度比ROM快,更因为现在的内核包含的功能多并且文件数量增加,因而需要的存储空间很大,一般都在20MB左右。再加上其它开发商开发的应用程序文件,要求的空间就更大了。CE启动时内核镜像由加载程序解压并将系统文件加载到RAM的NK,NK是在config.bib中定义的一段RAM区域,专用于保存内核镜像解压出来的所有文件。Windows CE将NK看作是ROM,当执行一个应用程序时,CE内核将这个应用程序需要的系统DLL(在NK中保存)加载到Slot 1(地址范围0x0200 0000-0x03FF FFFF,在Windows CE.NET中Slot 1专用于XIP DLL使用)。Slot 1是一段虚拟地址,当CPU执行DLL的代码时,CPU会根据地址映射关系到NK中寻找实际的代码执行,因为NK是一段实际的物理内存,I/O速度非常快,所以相对于在ROM中执行,DLL的运行效率得到很大提高。
RAM和ROM文件系统是Windows CE默认的文件系统。RAM文件系统的优点是支持文件压缩、支持事务机制(和数据库中的事务机制相似)、数据I/O较快。Windows CE.NET启动时把除了NK以外的RAM分为对象存储(object store)区域和应用程序内存(program memory)区域,并且默认各使用一半RAM。在基于Windows CE的设备没有采用永久存储器之前,对象存储的作用相当于永久存储器,对象存储区域采用RAM文件系统来保存文件,对象存储中可以存储的对象类型有文件、目录、数据库、记录、数据库卷。默认在对象存储中存储的对象全部是压缩的。当整个系统关闭时,设备的电源还继续提供电力给RAM,这样对象存储中保存的所有数据就不会丢失。应用程序内存区域留给所有应用程序运行时使用。基于Windows CE的设备采用永久存储器后,对象存储的作用就被永久存储器替代了,所以采用永久存储器后,应该减小对象存储区域的大小。如果定制的Windows CE的内核包含了资源管理器,那么打开“控制面板”,在“系统”-“内存”中,可以调节这两个存储区域的比例。滑块向左,则释放对象存储区域的一些可用内存并将这些内存划到应用程序内存区域中。滑块向右则相反。
Windows CE.NET只能管理512MB的物理内存和4GB大小的虚拟地址空间。不同的CPU内存管理方法也不同。对于MIPS和SHX系列CPU来说,物理地址映射是由CPU完成的,CE内核可以直接访问512MB的物理内存。对于x86系列和ARM系列的CPU来说,在内核启动过程中它会将现有物理内存地址全部映射到0x8000 0000以上的虚拟地址空间中供内核以后使用。OEM可以通过OEMAddressTable来详细定义虚拟地址和物理地址的映射关系。OEMAddressTable本身并不是一个文件,它只是存在于其它文件中描述虚拟地址和实际物理地址的映射关系的数据。比如文件oem init.asm中包含一段代码:dd 80000000h, 0, 04000000h。它表示将整个物理地址(0x0400 0000=64MB)共64MB映射到虚拟地址从0x8000 0000到0x8400 0000中。
当一个应用程序启动时,内核为这个程序选择一个空闲的槽(Slot),并且加载所有的代码、资源,并分配堆栈,加载DLL等。当这个进程得到CPU使用权时,它的整个地址空间被内核映射到Slot0,也就是当前进程使用的地址空间,然后开始运行。图中给出的地址实际上是经过映射到Slot 0之后的结构。从图中可以看出,进程首先加载代码段,因为每个进程最低部64KB作为保留区域,所以代码段从0x0001 0000开始,内核为代码段分配足够的虚拟地址空间后,接着分配空间为只读数据和可读/可写数据,接着分配空间为资源数据,之后分配空间为默认堆和栈。非XIP DLL从进程最高地址向下开始加载。非XIP DLL的加载按如下规则:内核先检查要加载的DLL是否被其它进程加载过,如果加载过,就做一个地址的重定位。这样就避免了整个系统内多次加载相同DLL。如果没有加载过,就按照从槽的高地址到槽的低地址的顺序查找空闲地址空间。然后分配足够的地址空间用于加载DLL。因为每个进程在执行前都要映射到Slot 0,而且进程使用的所有DLL可能来自不同的槽(Slot),为避免所有使用的DLL在映射到Slot 0中出现地址空间冲突的现象,内核的加载器(Loader)在加载DLL时会查找所有槽中加载的DLL的地址,保证在映射到Slot 0时不会发生地址冲突。假如系统内有两个进程,进程A只加载了DLL A,进程B需要加载DLL A和DLL B,那么进程B会留出DLL A的地址空间,然后加载DLL B,也就是说进程B映射到Slot 0时,DLL A的地址空间和DLL B的地址空间是相邻的,不会发生冲突。实际情况是Windows CE下DLL都很小,而且一个应用程序使用DLL多数是系统的DLL(存在于Slot 1)。所以目前来看进程的地址空间是够用的。
[参考文献]
[1]基于Windows CE 平台的嵌入式GIS 开发与应用,河南,2003.
[2]刘文峰,李程远,李善平.D嵌入式L inux 操作系统的研究.
[3]钱静,芦东昕.嵌入式软件虚拟内存管理技术的研究和实现,2008.
[4]付晓军,夏应清,何轩.嵌入式LwIP协议栈的内存管理,湖北,2008.