当系统创建一个进程并赋予地址空间时,可用地址空间中的大部分是闲置的或尚未分配的。为了使用这部分地址空间,我们必须调用VirtualAlloc函数来分配其中的区域。分配区域的操作被称为“预定”。
当应用程序预定地址空间区域时,系统会确保区域的起始地址正好是分配粒度的整数倍。分配粒度会根据不同的CPU平台而有所不同,但是到目前为止,所有的CPU平台都使用相同的分配粒度,大小为64KB。也就是说系统会把分配请求取整到64KB的整数倍。
当应用程序预定地址空间的一块区域时,系统会确保区域的大小正好是系统页面大小的整数倍。页面是一个内存单元,系统通过它来管理内存。与分配粒度相似,页面大小会根据不同CPU而有所不同。x86 和 x64系统使用页面大小为4KB,而IA-64系统使用页面大小为8KB。例如,应用程序试图预防一块大小为10KB的地址空间区域,那么系统会自动将该请求取整到页面大小的整数倍,然后用取整后的大小预定区域。这意味着在x86 和x64 系统中,系统会预定一块大小为 12 KB 的区域。
当程序不再访问所预定的空间区域时,应该释放该区域。这个过程被称为释放地址空间区域。通过调用VitualFree函数来完成。
为了让32位的程序能在64位版本的Windows运行,微软提供了一个Windows 32-bit On Windows 64-bit的模拟层,又称WOW64。
判断程序是否在WOW64运行:
BOOL WINAPI IsWow64Process(
__in HANDLE hProcess,
__out PBOOL Wow64Process
);
只有当32位应用程序在WOW64上运行时参数Wow64Process才会被设为TRUE。
在这种情况下,我们需要调用GetNativeSystemInfo 函数来获得真正的64位系统信息。直接调用GetSystemInfo返回的值和它在64位应用程序中运行所返回的值可能会有所不同。
void WINAPI GetNativeSystemInfo(
__out LPSYSTEM_INFO lpSystemInfo
);
在应用程序中使用虚拟内存
预定地址空间区域:
LPVOID WINAPI VirtualAlloc(
__in LPVOID lpAddress,
__in SIZE_T dwSize,
__in DWORD flAllocationType,
__in DWORD flProtect
);
lpAddress参数指向内存地址,用来告诉系统我们要预定地址空间中的哪一块。在大多时候只要传NULL就可以了,系统会自动找一块闲置区域。
dwSize 参数用来指定我们想要预定的大小,以字节为单位。
flAllocationType 参数用来告诉系统我们到底要预定区域还是调拨物理存储器。假如我们是要预定空间,并想让系统从尽可能高的内存地址来预定区域,那么我们必须给lpAddres传NULL,并且传MEM_RESERT | MEM_TOP_DOWN 标志给flAllocationType 参数
flProtect参数是给区域指定保护属性。最常用的是PAGE_READWRITE。
给区域调拨物理存储器
在预定了区域后,我们还需要给区域调拨物理存储器,这样才能访问其中的内存地址。系统会从页交换文件中来调拨物理存储器给区域。在调拨物理存储器时,起始地址始终都是页面大小的整数倍,整个页面大小也是页面大小的整数倍。
为了调拨物理存储器,我们必须再次调用VirtualAlloc。但这次我们会传MEM_COMMIT来做为第三个参数flAllocationType的值。再给物理存储器指定页保护属性的时候,通常我们使用保护属性会和预定区域时相同。
在已经预定的区域中,我们必须告VirtualAlloc函数要调拨多少物理存储器给哪里。这是通过参数lpAddress和dwSize 来决定的。值得注意的是,我们无须一下子给整个区域都调拨物理存储器。
下面给出一个例子:
假如我们的应用程序在地址 5242880 处预定了一块大小为512KB的区域。现在我们希望给该区域从2KB地址开始的地方调拨 6KB 的物理存储器。
代码如下:
VirtualAlloc((PVOID) (5242880 + 2 * 1024), 6 * 1024, MEM_COMMIT, PAGE_READWRITE);
在这种情况下,我们必须调拨8KB的物理存储器。
同时预定和调拨物理存储器
有时,我们想要同时预定区域并给该区域调拨物理存储器。只要调用VirtualAlloc一次就能达到这一目的。
PVOID pvMem = VirtualAlloc(NULL, 99*1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWITE);
撤销调拨物理存储器及释放区域
BOOL WINAPI VirtualFree(
__in LPVOID lpAddress,
__in SIZE_T dwSize,
__in DWORD dwFreeType
);
lpAddress参数应为VirtualAlloc 返回的地址。
dwSize 参数必须传0;
dwFreeType 参数必须传MEM_RELEASE.