刚开始这个话题,引用书上的一段话:在编写Microsoft Windows CE 程序时首要需要关注的是什么呢?那就是如何处理内存。一台Windows CE设备可能只有16MB的RAM。相对于标准的个人电脑通常的512MB甚至更多的内存,这简直少的可怜。事实上,由于Windows CE设备上的内存十分缺乏,以致于在编写程序的时候有时需要为节约内存而牺牲应用程学的整体性能。引用这段话,让我们明白了,在Windows CE 程序设计的过程中,我们无时无刻都需要注意内存的管理,这就显示出了内存管理的重要性。
一.Windows CE的内存基础知识
Windows CE 中既有RAM也有ROM,在Windows CE中,RAM的一部分被用来作为对象存储(如注册表信息),ROM就相当于一个小型的磁盘,并且会用它来存储整个操作系统。在ROM中的程序还可以被设计为本地执行(XIP).在后面的内存中就会看到相关的信息。
Windows CE实现了虚拟内存管理系统,也就是由MMU的硬件支持,所以Windows CE必须要有MMU.MMU主要是进行一些地址转换和权限检查的功能。
Windows CE也是一个基于分页的操作系统,每一个页的大小为4096个字节。虚拟页面可以处于3种状态:空闲(free),保留(reserved),和提交(committed).如果需要查询系统的内存情况可以通过:
Void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
函数来查询,里面包括可用的虚拟内存,可用的物理内存,总的虚拟内存,总的物理内存。
介绍完了大概的一些情况,下面主要通过Windows CE 5.0和6.0版本来介绍一下Windows CE的虚拟内存分配。
这张图是一个总的虚拟内存空间,左边的是5.0版本,右边的是6.0版本。在Windows CE当中,可以访问的虚拟内存空间一共为4G,这4G被分为了两部门,上面2G被内核占用,下面2G是引用程序的地址空间。下面来分别介绍:
Windows CE 5.0版本的应用程序地址空间:
Windows CE 5.0中,把地位的1G分为了34个槽(Slot),每个槽占用了32MB,这样就限制了Windows CE 一共可用的进程数为32个,系统NK.exe,device.exe,gwes.exe,file.exe要占用四个进程,所以一共可用的进程数只有20多个。这是5.0的一个限制。0号槽作为当前运行进程的槽,当某个进程需要调度的时候,就将它的进程空间拷贝到Slot 0 中。Slot 1 被用来加载基于Rom的动态链接库和制度只读的内存,这32MB的空间为所有正在运行的进程所共享.下图有一个详细的介绍:
Windows CE 6.0版本的应用程序地址空间:
Windows CE 6.0对虚拟内存空间进行了一个重写,由于5.0有这32*32的限制。6.0中每个运行着的进程有着独立的2G内存空间,其实这2G内存空间,高位的1G是所有内存所共享的,低位的1G才是进程所独有的。他们的分配方式有兴趣的可以参考demand Paging.
Windows CE 6.0的内核地址空间也有了一定的改写,在6.0中将应用进程空间的一些东西放入了内核空间。
二.Windows CE的不同内存分配
Windows CE 有着很多不同内存的分配方法。
上图显示了所有的内存分配方式,在我刚开始学习的时候,也不是很明白为什么有着这么多种的分配方式。其实就一句话:越往下面的内存分配方式效率越高,但是应用的复杂度也就越高。
虚拟内存分配可以用以下方法来实现:
分配函数:
LPVOID VirtualAlloc(LPVOID, DWORD, DWORD,DWORD)
释放函数:
LPVOID VirtualFree(LPVOID, DWORD, DWORD)
改变和查询访问权限函数:
LPVOID VirtualProtect(LPVOID, DWORD, DWORD,DWORD)
这3个函数具体的参数可以查看MSDN,在这里就不详细介绍了。在使用这3个函数的时候我们需要注意一些问题,用个书上的例子来解释:
for(i = 0; i < 512; i++)
{
pMem[i] = VirtualAlloc(NULL, PAGESIZE,
MEM_RESERVE | MEM_COMMIT,
PAGE_RDWRITE);
}
也许在XP下我们写这种程序也成习惯了,但是这是在Windows CE中,这里我们就需要注意一些问题了:
1.保留的虚拟内存是按照64KB对齐的。
2.提交的虚拟内存是按照页面来对齐的。
大家也许就已经明白过来了,如果我们这样去申请内存,在系统自动内存对齐的情况下我们是会浪费非常多的内存的,在5.0中测试这个程序,分配会失败,在6.0中分配成功,大家可以去试验一下。那么我们正确的做法应该是先保留一个大的内存块,然后再需要用的时候再提交一次。
pMemBase = VirtualAlloc (NULL, PAGESIZE, MEM_RESERVE,
PAGE_REDWRITE);
for(i = 0; i < 512; i++)
{
pMem[i] = VirtualAlloc (pMemBase + (i * PAGESIZE),
PAGESIZE, MEM_COMMIT,
PAGE_REDWRITE);
}
通过这些学习也给了我们一定的启示:
CE 5.0的32*32的局限:
1.尽可能少使用多进程。
2.可以多利用多线程。
3.使用内存映射文件区域申请大内存
虚拟内存分配的64KB对齐:
1.尽可能按照64KB边界申请虚拟地址空间。
2.一次申请,多次提交。
3.使用更上层的动态内存分配函数。
4.合并小的DLL为64KB边界。
堆和栈的内容就不在这里介绍了。
三.总结
我们已经讨论了不同的内存类型,现在来讨论如何充分利用各种内存类型。
1.对于大的内存块,最好是直接分配虚拟内存。
2.本地堆使用便利,但是需要注意内存碎片。
3.独立堆的优点是当你不使用时可以直接销毁它,从而把内存碎片消灭在萌芽状态。
4.静态数据区域是放一两个缓冲区的好地方,因为页面总是要被分配的。
5.栈使用简单,但是栈的大小只有64KB。