技术开发 频道

Windows CE .NET 高级内存管理

 限制因素

 尽管 Windows CE 应用程序的一个限制因素是可用于应用程序的 RAM 数量,但还有另一个主要的限制是应用程序的相对很小的 32 MB 虚拟地址空间。尽管 XIP DLL 被加载在 32 MB 空间的上端,但所有其他内存分配和任何基于 RAM 的 DLL 都必须能够放进应用程序的 32 MB 内存空间中。这个 32 MB 限制“框”不是 Windows CE 程序员所面临的很大问题,因为它是一个可被克服的挑战。

 要了解这个看似很大的内存空间有些什么限制性,必须了解 VirtualAlloc API 的操作原理。VirtualAlloc 是任何 Microsoft Win32 操作系统中最基础的内存分配调用。它在页级别分配内存;页是可以被 CPU 分配或释放的最小的内存单位。Windows CE .NET CPU 的页大小是 1024 或 4096 字节,这取决于 CPU。最广泛使用的是 4 KB 页大小。

 VirtualAlloc 调用分配内存的过程分为两个步骤。第一步,保留虚拟内存空间的区域。这种保留不会消耗任何 RAM;它只是防止一部分虚拟地址空间被用于其他用途。保留内存空间之后,就可以提交 (commit) 部分或整个区域,这个过程是指将实际物理内存映射到保留区域。VirtualAlloc 函数用于保留内存空间和提交内存。下面显示了 VirtualAlloc 函数的原型。

 LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize,

 DWORD flAllocationType,

 DWORD flProtect);

 VirtualAlloc 的第一个参数是要分配的内存区域的虚拟地址。使用 VirtualAlloc 提交先前保留的内存块时,使用 lpAddress 参数来标识先前保留的内存块。如果该参数是 NULL,则由系统确定从哪里分配内存区域,并以 64 KB 为边界。第二个参数是 dwSize,它是要分配或保留的区域的大小。因为该参数是以字节而不是页为单位指定的,所以系统会将所请求的大小以下一个页边界为限进行舍入。

 flAllocationType 参数指定分配的类型。可以指定以下标志的组合:MEM_COMMIT、MEM_AUTO_COMMIT 和 MEM_RESERVE。MEM_COMMIT 标志用于分配程序使用的内存。MEM_RESERVE 用于保留要随后提交的虚拟地址空间。保留页是无法访问的,直到通过指定区域并使用 MEM_COMMIT 标志进行了另一个 VirtualAlloc 调用为止。MEM_AUTO_COMMIT 标志唯一用于 Windows CE 并且很好用,但它不是本文的主题。

 因此,要使用 VirtualAlloc 来分配可使用的 RAM,应用程序必须调用 VirtualAlloc 两次,一次保留内存空间,再一次则提交物理 RAM;或者调用 VirtualAlloc 一次,这需要在 flAllocationType 参数中组合使用 MEM_RESERVE 和 MEM_COMMIT 标志。

 组合保留和提交标志方式所使用的代码更少,并且更快、更简单。该技术通常用在 Windows XP 应用程序中,但用在 Windows CE 应用程序中不是很好。以下代码片段演示了存在的问题。

 INT i;

 PVOID pMem[512];

 for (i = 0; i < 512; i++) {

 pMem[i] = VirtualAlloc (0, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT,

 PAGE_READWRITE);

 }

 该代码片段似乎是无害的。它分配了 512 块内存,每块内存的大小为 1 页。问题是:在 Windows CE 系统上,甚至是在有数兆字节可用 RAM 的系统上,该代码总是会失败。问题在于 Win32 操作系统保留内存区域的方式。

 在任何 Win32 操作系统(包括 Windows CE .NET)上,当一个虚拟内存空间区域被保留时,它会将保留区域对齐 64 KB 边界。因而,上面的代码片段试图将保留的 512 个区域的每个区域都对齐 64 KB 边界。Windows CE 应用程序的问题是它们必须位于 32 MB 虚拟内存空间的范围内。在整个应用程序内存空间中该空间的大小只有 512 64 KB,并且它们中的一部分需要用作应用程序代码、本地堆、堆栈和应用程序所加载的每个 DLL 的区域。通常,在对 VirtualAlloc 进行大约 470 次调用之后上面的代码片段将失败。

 上述问题的解决方案是首先保留足够用于整个分配的较大区域,然后在需要时提交 RAM,如下所示。

 INT i;

 PVOID pBase, pMem[512];

 pBase = VirtualAlloc (0, 512*PAGE_SIZE, MEM_RESERVE, PAGE_READWRITE);

 for (i = 0; i < 512; i++) {

 pMem[i] = VirtualAlloc (pBase + (i * PAGE_SIZE), PAGE_SIZE,

 MEM_COMMIT, PAGE_READWRITE);

 }

 避免该问题的关键是知道这个情况。这只是 Windows CE 应用程序的地址空间中只有 512 个区域所带来的问题影响应用程序的很多地方中的一个。

 分配大型内存块

 Windows CE .NET 应用程序的地址空间局限于 32 MB 所引起的另一个问题是如何分配大型内存块。当应用程序的整个地址空间被限制在 32 MB 以内时,如果应用程序需要一块 8、16 或 32 Mb RAM 用于具体用途,它怎样才能分配该内存?回答是应用首先用在 Windows CE .NET 早期版本中针对视频驱动程序的一个修复程序。有了它,如果 Windows CE .NET 检测到一个对 VirtualAlloc 的调用请求保留超过 2 MB 的地址空间,该地址空间将不会保留在 32 MB 的限制大小中。该内存块将保留在大型内存区域 (Large Memory Area) 中,大型内存区域位于全局内存空间中,正好在 2 GB 系统保留空间的下面。

 内存空间已经保留后,应用程序就可以通过调用 VirtualAlloc 来提交在保留空间内的具体页。这就允许应用程序或驱动程序使用大型内存块,即使它存活在 32 MB 的大小的限制内。下面的代码显示了分配 64 MB 块然后提交保留区域的一页的简单情形。

 

  前面的代码还显示了直接处理虚拟内存 API 所具有的一个特性。这就是您可以创建大型稀疏数组,而不会消耗大量 RAM。在上面的代码中,64 MB 保留区域不会消耗任何物理 RAM。在该示例中,唯一被消耗的 RAM 是在第二次调用 VirtualAlloc 以提交页时使用的一个页(4096 字节)。

PVOID ptrVirt, ptrMem;

 ptrVirt = VirtualAlloc (0, 1024 * 1024 * 64, MEM_RESERVE,

 PAGE_NOACCESS);

 if (!ptrVirt) return 0;

 ptrMem = VirtualAlloc ((PVOID)((int)ptrVirt+4096),

 4096, MEM_COMMIT, PAGE_READWRITE);

 if (!ptrMem) {

 VirtualFree (ptr, 0, MEM_RELEASE);

 return 0;

 }

 return ptrMem;

 

0
相关文章