微软有一篇文档
《Virtual Memory Layout: Windows CE 5.0 vs. Windows Embedded CE 6.0 》
非常不错
这里特意翻译成中文给大家参考
WinCE5与WinCE6的虚拟内存布局
wwfiney@ARMCE
在WinCE6之前,进程(process)有最多32个,及每个进程只能有32MB虚拟内存(virtual memory)的限制,并且所有的进程共享相同的4GB地址空间。对于CE6,内核进程存在于4GB(32-bit)虚拟地址空间的高2GB空间内,并且底部的2GB空间对于每一个进程都是唯一的。由于可创建的句柄数量的原因,进程有最多32000个的限制。但是实际应用当中,限制进程数量的往往是物理内存大小。
在以前版本的WinCE中,当前进程使用Slot0。在WinCE6里面,Slot0和其他进程Slot一样,都各有2GB的有效空间。
由于虚拟内存的存取是硬件通过内存管理单元(MMU)来实现的,所以虚拟内存编码是CPU相关的。与其他使用软件TLB(translation look-aside buffer)的CPU体系不同,ARM和X86体系的CPU使用硬件页表,所以虚拟内存的存取是通过硬件直接进行的。
下面列出WinCE6在虚拟内存管理方面的设计目标:
• 每个进程拥有大的虚拟内存
• 没有预设的进程数限制
• 进程间的保护
• 最小的CPU相关代码
• 高效的虚拟内存分配
• 高效的TLB miss处理
下表列出CE6.0的虚拟内存分布
模式 地址范围 大小 描述 注释
内核模式 0xF0000000 - 0xFFFFFFFF 256 MB CPU特定的虚拟内存空间 系统呼叫陷阱区域
内核数据页
内核模式 0xE0000000 - 0xEFFFFFFF 256 MB 内核虚拟地址 CPU相关 内核空间虚拟地址 有些CPU体系禁止了这块地址,如SHx
内核模式 0xD0000000 - 0xDFFFFFFF 256 MB 内核虚拟地址 内核空间地址,由所有被内核加载的服务和驱动共享
内核模式 0xC8000000 - 0xCFFFFFFF 128 MB 对象存储(Object store) RAM存储空间,为RAM文件系统,CEDB数据库,RAM注册表服务
内核模式 0xC0000000 - 0xC7FFFFFF 128 MB 内核XIP的DLL XIP的Dll,服务于内核及所有由内核加载的服务和驱动
内核模式 0xA0000000 - 0xBFFFFFFF 512 MB 非缓存的静态映射地址 不通过CPU缓存直接访问物理内存
内核模式 0x80000000 - 0x9FFFFFFF 512 MB 缓存的静态映射地址 通过CPU缓存来访问物理内存
用户模式 0x7FF00000 - 0x7FFFFFFF 1 MB 未映射,作为保护 用户模式与内核模式之间的缓冲地址
用户模式 0x70000000 - 0x7FEFFFFF 255 MB 共享的系统堆(heap) 内核与进程之间共享的堆(heap)空间 内核及其服务可以在这个堆上写入 用户进程只能读取(Read-only) 允许一个进程从服务端(server)获得数据而不用进行一次内核呼叫
用户模式 0x60000000 - 0x6FFFFFFF 256 MB 使用RAM的映射文件(RAM-backed map files) 为了向后兼容,使用RAM的映射文件被映射在固定在位置。
使用RAM的映射文件是指那些没有分配实际文件的内存映射文件
调用CreateFileMapping,并且hFile为INVALID_HANDLE_VALUE时可以得到这种内存映射文件
这个区域是给使用这种内存映射文件来作为进程间通信手段的应用程序提供向后兼容
用户模式 0x40000000 - 0x5FFFFFFF 512 MB 用户模式的Dll代码及数据 Dll是从地址底部开始加载并往上增长
• 起始地址为0x40000000.
• 代码和数据是交织在一起的
• 被多个进程加载的同一个dll在各进程间使用相同的地址
• 代码页共享相同的物理内存页
• 每个进程有自己独立的物理内存页给数据页部分
用户模式 0x00010000 - 0x3FFFFFFF 1 GB 进程及用户分配的虚拟地址 进程的可执行代码及数据
用户的虚拟内存分配起始地址是在进程的代码及数据段之后,并且是向上增长(言下之意代码及数据段是放在最下面)
用户模式 0x00000000 - 0x00010000 64 KB CPU相关的用户内核数据 对于用户而言,用户内核数据永远是只读的
根据CPU体系的不同,用户内核数据可以被内核读写(ARM),或者只读(其他CPU体系)
关于上面的表格有以下几个要点需要注意:
• 用户模式Dll现在是从0x40000000开始往上(高地址)增长,而不是往下增长了
• 用户调用虚拟内存相关的函数只能操作由VirtualAlloc分配的空间,而不能再操作内核模式分配在用户空间的虚拟内存,例如栈(stacks),内存映射文件(memory-mapped file views),以及Dll代码及数据
• 共享堆区域对于内核是可读写的,而对于用户进程是只读的。这块区域是用来提高系统与客户端进程通信效率的。但是由于所有进程都可以读取这块区域,所以不要放任何安全相关的数据,比如密码等
• 区域之间至少有64KB的没有映射的空间以防止越界访问(注:这里的区域之间我理解为内核与用户模式之间,即那1MB的空间)
用户进程的虚拟内存接口
在某一时刻,一个虚拟地址只能被映射一次。你不能通过其他进程的虚拟地址直接存取该进程的存储区。虚拟内存函数只能用在由一个进程分配的地址上。举例来说,一个应用程序不能调用VirtualProtect去改变自己的代码段的保护属性
带Ex的函数版本是服务端用来存取客户端进程虚拟地址的
下面这些函数组成了用户进程可用的虚拟内存操作接口:
• VirtualAlloc
• VirtualAllocEx
• VirtualFree
• VirtualFreeEx
• VirtualProtect
• VirtualQuery
• WriteProcessMemory
• ReadProcessMemory
• VirtualProtectEx
• VirtualQueryEx
内核模式虚拟内存操作接口
下面带Ex的函数版本使用进程句柄作为参数,来操作其他进程的虚拟内存。下表是内核模式的虚拟内存操作接口:
• VirtualCopy
• VirtualCopyEx
• LockPages
• UnlockPages
• CeVirtualSharedAlloc
• AllocPhysMem
• CreateStaticMapping
• VirtualSetAttributes
• DeleteStaticMapping
使用这种虚拟内存布局的好处
• 进程总数不再限定在32个
• 每个进程可用的地址空间不再限定在32MB
• 由于使用硬件页表,ARM和X86结构的CPU在进程切换上速度更快了,但是MIPS和SHx的CPU几乎没有变化
• 由于简化了地址空间存取检查,TLB miss处理在MIPS和SHx的CPU上速度有所提升,但是ARM和X86上几乎没有变化
虚拟内存布局带来的折衷
• 各进程的虚拟内存空间不再总是可以访问的了,但是内核进程和当前用户进程的虚拟内存空间却总是可以访问的。因此,存取其他进程的存储空间,特别是传递给服务端的buffer,已经不是进行简单的映射指针就可以处理了
• 更复杂的引用计数
• 更复杂的进程间通信和buffer传递