二、 内核存储器空间
内核存储器空间是Windows Embedded CE 6.0虚拟地址空间中上面2GB的部分,在操作系统运行的整个过程中,对于所有进程它总是驻留的,它包含了提供更多基本操作系统服务的组件。内核空间详细的虚拟存储器映射如图3所示,对每个存储器区域的详细描述如表1所示。
图3 内核存储器空间
表1 内核存储器空间描述
范围 | 大小 | 描述 | 说明 |
0xF0000000 - 0xFFFFFFFF | 256MB | 特定于CPU的虚拟内存 | 系统调用捕获区域,内核数据页 |
0xE0000000 - 0xEFFFFFFF | 256MB | CPU相关的内核虚拟内存 | 内核空间虚拟内存(除非CPU不允许,如SHx) |
0xD0000000 –0xDFFFFFFF | 256MB | 内核虚拟内存 | 内核空间虚拟内存,由所有的内核服务和加载在内核的驱动程序共享 |
0xC8000000 - 0xCFFFFFFF | 128MB | 对象存储 | 用于存储文件系统、CEDB数据库和注册表 |
0xC0000000 - 0xC7FFFFFF | 128MB | 内核XIP DLL | 用于所有加载在内核中可就地执行(eXecute In Place)的DLL,包括内核服务器和驱动程序等 |
0xA0000000 - 0xBFFFFFFF | 512MB | 静态映射(Uncached) | 跳过CPU Cache直接访问物理内存 |
0x80000000 - 0x9FFFFFFF | 512MB | 静态映射(Cached) | 通过CPU Cache访问的物理内存 |
三、用户存储器空间
用户存储器空间是Windows Embedded CE 6.0虚拟地址空间中下面2GB的部分,这2GB的存储器空间对于每个进程都是唯一的,即每个进程都独占自己2GB的存储器空间。用户空间详细的虚拟存储器映射如图4所示,对每个存储器区域的详细描述如表2所示。
图4 用户存储器空间
表2 用户存储器空间描述
范围 | 大小 | 描述 | 说明 |
0x7FF00000 - 0x7FFFFFFF | 1MB | 未映射的保留区域 | 内核空间与用户之间的缓冲区 |
0x70000000 - 0x7FEFFFFF | 255MB | 共享的系统堆 | 内核与进程间的共享堆。内核和内核服务可以在此分配并进行读写操作;而用户进程只能进行读操作。这使一个进程不必进行内核调用就可以从内核服务获得数据 |
0x60000000 - 0x6FFFFFFF | 256MB | RAM后备的映射文件 | RAM后备的映射文件被映射到固定的位置。通过调用函数CreateFileMapping并为hFile参数传递INVALID_HANDLE_VALUE值来创建或获得 |
0x40000000 - 0x5FFFFFFF | 512MB | 用户模式的DLL代码和数据 | DLL由地址0x40000000开始向上加载;代码和数据是相互交叉的;由多个进程加载的一个DLL在所有进程中都被加载到同一个地址位置;对于每一个进程,数据页有唯一的物理页 |
0x00010000 - 0x3FFFFFFF | 1GB | 进程用户可分配的虚拟内存区域 | 可执行代码和数据;用户虚拟内存(堆)从exe之上开始并向上分配 |
0x00000000 - 0x00010000 | 64KB | CPU相关的用户内核数据 | 用户内核数据对于用户总是只读的,而对于内核有可能是可读可写的(对于ARM CPU),也可能是只读的(对于其它CPU) |
在Windows Embedded CE 6.0中,当一个进程初始化时,操作系统映射下列DLL和内存组件:
l 一些可就地执行(XIP)的动态链接库(DLL)
l 其它可就地执行动态库(XIP DLL)的一些读/写区域
l 所有不能就地执行的DLL(non-XIP DLL)
l 栈(Stack)
l 堆(Heap)
l 进程的数据区域
在进程初始化时,DLL和ROM DLL的读/写区域被加载在从底部向上1GB开始的虚拟存储器空间,对于每个进程来讲,所有的DLL都被加载到相同的地址。同时,栈、堆和可执行文件(.EXE)在进程初始化时被创建并从地址空间的底部64K以上的空间开始被一一映射,而底部64K的范围总是保持为自由存储器空间。
RAM后备的映射文件也被称为内存映射文件,是一种把文件直接当作系统内存来使用的方法,这样既避免了使用文件I/O对文件进行访问的麻烦,又可以用来在多个进程间共享数据。尤其是对于几十兆甚至几百兆的大型文件,这种方法提供了一种直接映射存取的方便之道。当任何进程打开同一个RAM后备的映射文件时,都会获得相同的指针值,因而,RAM后备的映射文件的用户存储器区域为使用RAM后备的映射文件来进行进程间通信的应用程序提供了一种向后的兼容性。与RAM后备的映射文件相对的是文件后备的内存映射文件(File backed memory map files),文件后备的内存映射文件是从进程的虚拟内存空间区域分配的,因而对于不同的进程是不同的,进程间不能共享。
堆是专门为应用程序保留的一部分内存,应用程序可以以每次一个字节或每次4个字节的方式分配和释放这部分内存,同样,应用程序也可以在堆中简单地分配一个虚拟内存块,而让操作系统去决定到底需要分配多少物理内存页。在Windows Embedded CE 6.0中,每个应用程序在被加载运行时,操作系统都会为它创建一个默认的、大小为64KB的本地堆。除了本地堆,应用程序还可以创建私有堆、共享堆和远程堆。如果应用程序要在本地堆中分配一个大于64KB的内存块,系统会通过调用VirtualAlloc函数来满足对内存的要求。应用程序也可以创建任意数量的单个的私有堆,这些私有堆除了由一组单独的堆函数管理外,与本地堆具有相同的属性。共享堆允许内核模式的组件有效地传输数据给用户进程,且对于内核组件共享堆是可读可写的,而对于用户进程却是只读的。共享堆位于用户地址空间之上的位置,对所有用户进程都是可见的,因此要避免将一些敏感数据放入共享堆。远程堆是Windows Embedded CE 6.0的一个新特征,远程堆允许一个服务器与客户端进程安全地共享内存,服务器进程负责创建远程堆,并对远程堆有完全的访问权,而客户端进程只能读或有选择地写远程堆,且不能销毁远程堆中的元数据。
栈也是专门为应用程序保留的一部分内存,用于保存需要先进后出(FILO)或后进先出(LIFO)的数据。堆是由进程分配的,而栈是由线程分配的;每个进程至少有一个堆,而每个线程只有一个栈(如图5所示)。栈的大小是在应用程序编译时确定的,而栈的分配却是在线程创建时分配的。默认情况下,一个进程的所有线程具有相同的栈大小。栈的大小可以通过/STACK链接开关来指定,一个单独的线程也可以动态地创建唯一的栈大小。
图4-8 堆和栈之间的关系