http://space.itpub.net/12325938/viewspace-594680
近几天,我在开发一款嵌入式移动产品项目上遇到了一点麻烦。这个应用是ARM9内核和WinCE平台,这几天我一直被纠缠在一个内存问题,程序吃内存很厉害,跑一段时间后,内存占用就接近100%了,出现所谓的假死机。
看任务管理器里面的程序内存使用一直都很正常,但整个系统的内存却在被缓慢的侵蚀,但就是看不出来是程序哪一部份使用了过多的内存。反复的查程序,也没发现内存泄漏,需要不断的修改内存分配策略,很头疼。
1.WinCE是如何分配内存?ITPUB个人空间 f&Z gx4| GY a5QY
yTy)Cn6\\$?II&|0 (1)WinCE内存环境
.g,RYUS0 在PC上增加物理内存是很方便的,插上内存条后只要自检程序识别,桌面操作系统就能够支持,而在基于WinCE的产品上就没那么简单了。嵌入式设备与桌面PC的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存,这在驱动程序中对物理内存的访问尤为重要。尤其是像ARM体系结构下,I/O端口也被映射成某一个物理内存地址。而且与桌面版本Windows相比,WinCE也提供相对简单的物理内存访问方式,无论是驱动程序还是应用程序都可以通过API访问某一段物理内存。
一般来说,运行一个WinCE需要ROM(只读存储器)和RAM(随机存储器)。但在WinCE系统中,ROM和RAM的使用还是稍微有些不同于个人电脑环境。RAM在WinCE 系统中被分为两个区域:第一个是程序存储区(Program Memory),也叫做系统堆(System Heap),程序存储区有点像个人电脑中的RAM,它为正在运行的应用程序保存堆和栈的内容。第二个是对象存储区(Object Store),它有点像一个永久的虚拟RAM磁盘。不同于PC上的虚拟RAM磁盘,对象存储区保留存储的文件甚至可以在系统被关闭以后。
在WinCE系统中,存储在ROM的程序能以现场执行(Execute in Place,XIP)的方式运行。换句话说,程序可以直接从ROM中执行而不必先加载到RAM中再执行。这种能力对嵌入式系统来说,具有两个方面的巨大优势,一是代码直接从ROM中执行意味着程序代码不会占据更有价值的RAM;二是程序在执行前也不必先复制到RAM中,这样就只需要很少的时间来启动一个应用程序。而不在ROM中的但是被包含在对象存储区或闪存卡中的程序将不能以现场方式执行,它们将被复制到RAM中再执行。
(2)虚拟内存ITPUB个人空间!{:P"X@;Wc'i
电脑中所有运行的程序都需要经过内存执行,如果执行的程序很大或很多,就会导致内存消耗殆尽。为了解决这个问题,Windows运用了虚拟内存技术,以缓解内存的紧张。和大多数操作系统一样,WinCE也实现按需调页的虚拟内存机制。WinCE是32位的操作系统,支持4GB的虚拟地址空间。系统调用虚拟内存API来为其它类型内存分配内存,包括堆和栈。WinCE虚拟内存页可以处在三种状态:自由(free),保留(reserved),或被提交(committed)。
WinCE提供两种虚拟地址映射方法,分别为静态映射和动态映射,静态映射的虚拟地址空间只能由内核访问,而动态映射的地址空间可以由用户模式的应用程序访问。WinCE系统使用微处理器的内存管理单元(MMU)来处理虚拟地址和物理地址间的实时转换。意思是说:一旦WinCE系统的MMU开始工作,CPU就不再直接访问物理内存了,对于运行在x86和ARM系列CPU上的CE内核来说,必须先确立物理内存地址和虚拟内存地址的映射关系。ITPUB个人空间"@Y8TU9Qx
3|zb L/xFU0 这种关系是在一个名为OEMAddressTable的表中定义的,属于静态映射方法。因此,在一个虚拟内存系统中,应用程序主要处理这个虚拟的地址空间,并不涉及到由硬件管理的物理内存,应用程序使用虚拟内存不需要知道实际物理内存的位置,只要有内存可用就行。
2. WinCE与桌面WINDOWS在内存方面的区别
O ?C:{/h,ta!co0ITPUB个人空间;Hsb e)n
(1)Win32 API 接口之间的区别ITPUB个人空间&Qm,d,c1|"o
WinCE实现了Windows XP中可用到的几乎全部的Win32内存管理API。WinCE支持虚拟内存(virtual memory)分配,本地(local)和分离(separate)的堆(heaps),甚至还有(memory-mapped files)内存映射文件。虽然 WinCE 支持几乎每个Win32 内存管理函数,但是开发人员应当永远不要忘记 WinCE 是与 Windows XP 完全不同的操作系统,这些内存管理API的实现WinCE 具有不同的要求和不同的实现方式。
像Windows XP一样,WinCE支持一个带有应用程序间内存保护功能的32位地址空间,但是WinCE是被设计来应用于不同场合,所以它底层的内存结构不同于Windows XP。例如,WinCE不支持Win32 内存API的全局堆(global heap)。因此,开发人员应该熟悉 Win CE 与 Windows 桌面版本之间的细微差异,在设计应用程序或诊断问题时知道 WinCE 如何实现Win32 兼容性是一件非常重要的事情。
(2)存储结构区别ITPUB个人空间x1Fg{_Fh
WinCE实现了一个和其他Win32操作系统类似的分页式虚拟内存系统。在WinCE中,一页的大小可以从1024字节到4096字节,基于微处理器的不同而不同。这和Windows XP不同,Windows 页面尺寸是Intel微处理器所支持的4096字节。
WinCE 通过改变每页的保护来保护程序内存,而不是分配给每一程序不同地址空间。存储在ROM中的程序组在WinCE下当地执行,所以嵌入式系统的设计者能够只占用很少的RAM用于堆栈存储的需要。而且为了进一步的增加应用程序软件的性能,WinCE采用按需求将内存分页,操作系统仅仅需要解压缩并且装入基于RAM的一小部分程序准备执行。因此,ROM和基于RAM的程序的灵活性与速度意味着基于WinCE的设备能够被构造成多种内存结构形式。
(3)低内存环境的区别ITPUB个人空间K2D%T0z0j1f0Ygb
WinCE定义了内存门槛(Memory thresholds),它有四种内存状态:Normal, Limited, Low和Critical,这些状态的划分主要取决于当前可用的内存大小。WinCE监视系统自由的RAM,并对越来越少的RAM作出响应。当很少内存可用时,WinCE发送WM_HIBERNATE消息,接下来会限制可能的内存分配。当系统运行需要更多的内存,而又不能满足的情况下,shell会自动关闭该应用程序。
例如,OS首先向非活动状态的程序发送此消息,请求它们在不破坏各自内在状态情况下尽量释放多的内存,比如释放GDI对象;如果没有可用的物理RAM,需要栈空间的线程就会被挂起;如果在给定的一小段时间内,这个内存需求不能得到响应,就会弹出系统异常。简单说是:当系统运行在一个低RAM环境中,应用程序将调整并最小化它们的内存使用,如果当系统需要更多内存时,外壳(shell)会自动关闭这些程序。因此,管理WinCE运行在低内存系统中的程序的方法有很多种,但必须要小心应用,否则容易造成程序挂起或被关闭。
3. WinCE高效内存分配的策略
A"z H?2t%d!y"X0r3t0ITPUB个人空间:h%vjI&N'e
众所周知,知道从哪里查找问题是找到并解决问题的第一步。因此,通过熟悉 WinCE 如何管理内存,开发人员就能更快地避免缺陷和潜在的问题,和诊断问题。
(1)选择合适的内存类型ITPUB个人空间phzfH0[G2oj$Y
WindowsCE是组件化的操作系统,它可根据目标设备或平台的不同特点进行定制。原始设备制造商(OEM)或嵌入系统开发者可以选择所需的系统模块和组件,定制出各种不同性能的版本,而所选择的模块和组件确定了它的内存需求情况。例如,文件系统模块包括RAM文件系统、ROM文件系统、注册表和数据库几个组件,OEM可以组合不同的文件系统组件使之满足目标系统的需要。
因此,在各种组件的复杂整合中,在WinCE中比在桌面版本Windows可能有更多的机会导致内存分配失败。所以,为了更好的设计符合需求的紧凑型嵌入式系统,开发人员有必要知道所选择模块或组件要占用的内存情况,并选择合适的内存类型。
(2)合理分配可用内存是核心的一步
i;kW @ S |'E5ab0 WinCE编程最重要的方面是需要管理一个程序可用的有限的内存。其它的Windows编程可以假定几乎无限的内存空间,而WinCE应用程序必须尽可能减少所占用的内存空间以便在更少的内存环境中生存。在设计程序时,必须要根据分配给Windows CE的不同类型的内存来最大限度地利用它们。因此,在Windows CE中最小化内存使用的关键是选择与内存块使用模型相匹配的内存分配策略。
(3)根据系统定位设定内存分配策略和方案
b d to7^W0 究竟应用使用静态分配还是动态分配,一直是嵌入式系统设计中一个争论不休的话题。当然,最合适的答案是对于不同的系统采用不同的方案,不同的嵌入式系统应根据其不同的定位采用不同的内存分配策略。
程序运行时的内存分配有三种策略,分别是静态的、栈式的和堆式的。静态存储分配是指在编译时就能确定每个数据目标在运行时的内存需求。栈式存储分配也可称为动态存储分配,和静态存储分配相反,在栈式存储方案中程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,一般是按照先进后出的原则进行分配。ITPUB个人空间 m `|+jy"AH
iA-f IHgR]1cz0 堆是应用程序在运行的时候主动请求操作系统分配给自己内存,优点在于程序不必知道要从堆里分配多少内存空间。因此,用堆保存数据时会得到更大的灵活性,但这种灵活性会付出代价,就是在堆分配存储空间时会花掉更长的时间。
总的来说,采用静态分配会不可避免的使系统失去部分灵活性,故必须在设计阶段就预先知道所需要的内存并对之作出分配。如果出现没有预先考虑到的情况,系统就会无法处理,这样的分配方案必然会导致更大的内存浪费。因此,在WinCE系统设计时应采用静态分配与动态分配相结合的方法。