回炉重造之重读Windows核心编程-014-虚拟内存

83206837 2020-03-09

14.1 系统信息

GetSystemInfo函数用户检索与主机相关的值,只需要传递SYSTEM_INFO结构体的地址即可。

typedef struct _SYSTEM_INFO{
  union{
    DWORD dwOemId;
    struct {
      WORD wProcessorArchiteture;
      WORD wReserved;
    };
  };
  DWORD             dwPageSize; 
  LPVOID            lpMinimumApplicationAddress;
  LPVOID            lpMaximumApplicationAddress
  DWORD_PTR     dwArchitetureProcessorMask;
  DWORD             dwNumberOfPrcessors;
  DWORD             dwProcessorType;
  DWORD             dwAllocationGranularity;
  WORD              wProcessorLevel;
  WORD              wProcessorRevision;
}SYSTEM_INFO, *LPSYSTEM_INFO;

对于既定的系统,这些值不会改变,调用一次即可。

dwPageSize表示CPU的页面大小,x86的CPU上,这个值是4096个字节

lpMinimumApplicationAddress每个进程可用地址空间的最小内存地址。

lpMaximumApplicationAddress每个进程可用地址空间的最大内存地址。

dwAllocationGranularity保留地址控件的分配粒度,只要是Windows平台都是65536。

dwOemId已作废,不再使用。

wReserved保留给未来使用。

dwNumberOfPrcessors指明CPU的数目。

dwArchitetureProcessorMask位屏蔽,指明那个CPU是活动的。

dwProcessorType用于Windows98的,指明处理器的类型。

wProcessorArchiteture只用于Windows2000,指明处理器的类型。

wProcessorLevel只用于Windows2000,用于进一步细分处理器的结构。

wProcessorRevision只用于Windows2000,用于进一步细分处理器的级别。

14.2 虚拟内存的状态

函数GlobalMemoryStatus用于检索当前内存状态的动态信息。传递一个初始化后的MEMORYSTATUS结构的地址。

typedef struct _MEMORYSTATUS{
  DWORD dwLength;
  DWORD dwMemoryLoad;
  SIZE_T dwTotalPhys;
  SIZE_T dwAvailPhys;
  SIZE_T dwTotalPageFile;
  SIZE_T dwAvailPageFile;
  SIZE_T dwTotalVirtual;
  SIZE_T dwVirtual;
};

调用GlobalMemoryStatus前还必须将dwLength成员初始化成为字节能表示的结构的大小,即这个结构体的大小。这样未来添加结构体的成员就不会破坏现有的程序。调用GlobalMemoryStatus后,它对结构体中其余的成员赋值并返回。

在内存大于4GB的计算机上,或者合计交换的文件大小大于4GB,那么可以使用新的GlobalMemoryStatusEx

typedef struct _MEMORYSTATUS{
  DWORD dwLength;
  DWORD dwMemoryLoad;
  DWORDLONG dwTotalPhys;
  DWORDLONG dwAvailPhys;
  DWORDLONG dwTotalPageFile;
  DWORDLONG dwAvailPageFile;
  DWORDLONG dwTotalVirtual;
  DWORDLONG dwAvailVirtual;
  DWORDLONG dwAvailExtendedVirtual;  
};

这个新的结构所有成员的大小都是64位宽,因此它们的值可以大于4GB。最后一个成员ullAvailExtendedVirtual,用于指明在调用进程的虚拟地址空间的极大内存部分中未保留内存的大小,只用于某些配置中的某些CPU结构。

14.3 确定地址空间的状态

Windows提供的函数VirtualQuery,可以用来查询地址空间中内存地址的某些信息(如大小、存储器类型、保护属性等);另一个功能更强的VirtualQueryEx,能够查询另一个进程的内存信息:

DWORD VirtualQuery(
    LPCVOID pvAddress,
    PMEMORY_BASIC_INFORMATION pmbi,
    DWORD dwLength);

DWORD VirtualQueryEx(
  HANDLE hProcess, //待查询地址空间信息的进程句柄
    LPCVOID pvAddress,
    PMEMORY_BASIC_INFORMATION pmbi,
    DWORD dwLength);

这两个函数需要的同样的结构体MEMORY_BASIC_INFORMATION:

typedef struct _MEMORY_BASIC_INFORMATION{
  PVOID BaseAddress;// 跟pvAddress参数相同 但是四舍五入为页面的边界值
  PVOID AllocationBase;// 指明在BaseAddress的空间中的基地址
  DWORD AllocationProtect;//指明初始的保护属性
  SIZE_T RegionSize;//指明从基地址开始的所有页面的大小
  DWORD State;//所有相邻页面的状态,保护属性与pvAddress相同。
  //如果状态是空闲,AllocationBase、AllocationProtect、Type和Protect无意义。如果状态是MEM_RESERVE,在Protect无意义。
  DWORD Protect;//指明所有相邻页面的保护属性,与pvAddress相同。
  DWORD Type;//指明相邻存储器的类型。
}MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

最后一个参数是dwLength,用于设定MEMORY_BASIC_INFORMATION结构体的大小。

函数的返回值是拷贝到缓存中字节的数量。

14.3 .1 VMQuery函数

如果想知道已保留的地址空间区域的合计大小,或者想知道一个区域中的地址空间块的数量,或者想知道一个区域是否包含线程堆栈,那么仅仅调用一次VirtualQuery无法提供你想要的信息。

具体实现参见代码清单

相关推荐