翻译flywhc
原文见 Da_G的WM6.5 ROM制作教程:
http://forum.xda-developers.com/showthread.php?t=544445

除了原文中优化ROM的目的以外,在WM编程和破解时都要了解系统内存结构。


WinCE 5.2是WM6.5使用的操作系统,它与以前的6.1, 6.0在内存分配上有所不同

作为一个32位的系统,它有4GB的虚拟内存空间。这4GB分成两部分:2GB内核(kernel),2GB用户(user)。
内核内存只有内核级的才能访问,用户内存则可以被所有的进程和线程访问。用户内存地址空间是0x00000000 - 0x7FFFFFFF,内核的地址空间是0x80000000 - 0xFFFFFFFF。

用户内存被分为64个内存槽(slot 0),每个32MB。(64 X 32 = 2048,即2GB)。头33个内存槽(0, 1 - 32)可以用于所有的进程,其他的则用于对象、内存映射文件和资源映射。
用16进制地址表示很容易看出内存槽的地址分配:Slot 0: 0x00000000 to 0x01FFFFFF, Slot 1: 0x02000000 to 0x03FFFFFF, Slot 2: 0x04000000 to 0x05FFFFFF等。
每个进程都会被分配一个单独的内存槽,内核也要占一个内存槽Slot 0,因此还剩下32个可以给用户进程使用,这就是WM的32个最大进程数限制的由来,也是为何每个进程的最大使用内存只有32MB,因为每个内存槽只有 32MB。
实际上一个手机能运行的进程数量远远低于32,因为GDI/shell、驱动、服务系统程序等都是单独的进程。

当你添加一个模块(module,通常是个DLL文件),你等于告诉WM系统你想把那个模块进行内存映射,也就是每次都把这个模块装入到同一块内存中,这样可以节省Slot 0的内存。
这是在计算机上做的:用 wmreloc, g'reloc, bepe's Platform Rebuilder等工具定制ROM的时候也就做了这个映射。模块的虚拟内存分配是64KB边界对齐的,就是说如果你一个只有3KB的DLL文件,它也占了64KB的内存。

除此之外,系统还以4KB边界对齐方式分配内存页,用于进程和普通内存分配。系统处理的办法是:在创建ROM的时候,模块从上到下分配,比如slot 1里,第一个模块的顶地址从0x03FFFFFF开始分配,直到它64K的边界的首地址,接下来是第二个模块,直到分配到这个内存槽的首地址 0x02000000,有点像栈内存。
而普通内存(包括进程本身)则从下到上分配, 使用剩余的那些内存,比如还是slot 1,从0x02000000开始,一直到0x03FFFFFF,就像堆那样。
也就是说,在每个内存槽里放的模块越多,给程序可以自由分配的内存就越少。因此可以导致内存不足的错误:尽管还有的是物理内存,但虚拟内存空间耗尽了。

6.0/6.1/6.5的区别:6.5总共有4个内存槽可以用于模块分配, slot 0、1、60、61。另外63也可以用于不含代码的模块,比如只含资源的.mui文件。

WM6.0 只可以用 Slot 0、1 (先用1,然后0),总共64MB虚拟内存空间可以用于模块和内存分配,而且正在运行的进程总是映射到slot 0,如果用这块内存就相当于减少所有程序的可用内存。可以看到内存空间有多紧张。

WM6.1 改进了一些,slot 60和 61也可以用了,现在分配顺序为61、60、1、0。但是模块不能分配到那里面,只有文件可以。因此对ROM优化来说没有实质的改变,仍然只能用slot 1和0。
WM6.1还引入了进程initvmmap.exe,它位于MSXIPKernel, NK partition (xip.bin)。这个进程存有所有虚拟内存的映像,使用下面这个注册表键在boot hive (boot.hv)里面进行调整:

[HKEY_LOCAL_MACHINE\System\Loader\ModuleInfo]
[HKEY_LOCAL_MACHINE\System\Loader\ModuleInfo\dllname.dll]
"filename.exe"=dword:1
"filename2.exe"=dword:1

这个设置告诉系统,当只有在filename.exe或者filename2.exe运行的时候,dllname.dll才需要装入内存,在这两个程序没有运行的时候,dllname.dll所占的空间可以用于动态分配。

WM6.5的改进在于向模块开放了slot 60和 61,多有了64MB的内存空间用于模块映射。模块分配分配顺序为1、61、60、0,文件分配分配顺序为60、61、0。
为了让内核识别新的分配方案,必须使用新的6.5版的nk.exe。

Slot 0的可用虚拟内存越少,手机就越容易出现内存不足的错误,尽管不是真的内存不足。除此之外还有很多复杂事儿,比如平衡services.exe和 device.exe的负载,让他们每个进程的32MB的虚拟内存空间更好分配;还有把所有只有资源的DLL文件放到slot 63等等。

因此对于制作wm6.5 rom来说,支持新分配方案的重定位工具是很重要的,比如g'reloc就做不到给60/61分配模块。到目前为止wmreloc 2.0, and bepe's Platform Rebuilder 可以支持slot 60/61。

那么我们的目标是什么呢?(除了没有蛀牙外)
让slot 0越空越好,WM6.5 NK能让你装入更多的模块,不占用slot 0的空间,还有更快的装入速度。




译者后记:

照着这篇的内容试着画了一个内存表,转载请把字体设置为等宽字体比如黑体否则表格会乱掉:

┏━━━━━━━━━━━━━━┓FFFFFFFF
┃Kernal        ┃
┣━━━━━━━━━━━━━━┫80000000
┃SLOT63RESOURCE┃Resource files
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨7E000000
┃SLOT62 ???     ┃
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨7C000000
┃SLOT61 XIP     ┃
┃SLOT60 DLLS   ┃
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨78000000
┃SLOT59        ┃
┃Memory map    ┃Memory mapped files
┃SLOT33         ┃
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨40000000
┃SLOT32         ┃
┃ ……            ┃
┃SLOT2其他进程     ┃
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨04000000
┃SLOT1 XIP DLLs┃
┠┄┄┄┄┄┄┄┄┄┄┄┄┄┄┨02000000
┃SLOT0当前程序      ┃
┗━━━━━━━━━━━━━━┛00000000





WinCE的这种虚拟内存分配方式的目的是快速进程空间切换,不用做任何查表就可以直接切换不同进程的空间。但在内存价格降低,多媒体数据增加的今天有些过时了。
那么.net CF怎么样呢?好消息是,.net的DLL根本不受这个限制,因为Managed DLL根本不是DLL,只是个数据文件,系统把他们装入1GB映射共享内存区里,EXE文件也一样。
JIT会将IL代码从1GB的区拉到槽内存里进行编译,但是资源文件则不会被拉到内存槽里。

我们可以看到WM6.5不但对界面有很大的改进,对系统性能也有很大提高。


延展阅读:
http://blogs.msdn.com/hegenderfer/archive/2007/08/31/slaying-the-virtual-memory-monster.aspx
http://blogs.msdn.com/robtiffany/archive/2009/04/09/memmaker-for-the-net-compact-framework.aspx