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

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无法提供你想要的信息。

具体实现参见代码清单

原文地址:https://www.cnblogs.com/leoTsou/p/12446269.html

时间: 2024-10-31 03:54:36

回炉重造之重读Windows核心编程-014-虚拟内存的相关文章

回炉重造之重读Windows核心编程-003-内核对象

内核对象是个比较难理解的概念,问题的根源就在于即使是<核心编程>书中也没有说清楚它的定义,只是不停地举例和描述它的性质,还有如何使用. 盲人摸象,难见全貌.只能尽可能列举它的性质,注意使用了. 引用计数(书中的说法是使用计数)就是内核对象的一个很关键的性质.由于内核对象的拥有者是内核而不是进程,所以只能由内核来做撤销内核对象的操作.而通常一个内核对象不一定只被一个进程使用的,创建或者撤销内核对象,就要看引用计数了.引用计数在内核对象被创建的时候被置为1,被进程访问一次引用计数就递增1.当引用计

回炉重造之重读Windows核心编程-006-线程

线程也是有两部分组成的: 线程的内核对象,操作系统用来管理线程和统计线程信息的地方. 线程堆栈,用于维护现场在执行代码的时候用到的所有函数参数和局部变量. 进程是线程的容器,如果进程中有一个以上的线程,这些线程将共享进程的地址空间,操作空间中的数据,执行相同的代码,对相同的数据操作,甚至内核对象句柄(因为它是依托进程而不是线程存在的). 所以进程使用的系统资源比线程多的多,线程只需要一个内核对象和一个堆栈.既然线程比进程需要的开销少,因此始终都应该设法用增加线程来解决编程问题.当然这也不是一成不

回炉重造之重读Windows核心编程-001-错误处理

Windows处理错误靠的是API的返回值,类型不止一种种: VOID,函数不可能失败,Windows API的返回值很少是这个情况. BOOL,如果函数失败,则返回值是0,否则返回是非零值.不要测试返回值是否为TRUE! HANDLE,如果函数失败,则返回值通常是NULL,否则返回一个HANDLE用于操作对象.有的函数是返回INVALID_HANDLE_VALUE的,它被定义为-1,以函数在文档中的说明为标准! PVOID,如果函数失败,则返回NULL,否则返回内存块的地址. LONG/DWO

回炉重造之重读Windows核心编程-007-线程的调度、优先级与亲缘性

Windows被设计成一个抢占式的操作系统,用某种算法来确定哪些线程应该在何时被调度和运行多长时间.每隔20ms左右,Windows就要查看当前所有线程的内核对象,找到可以被调度的一个,将它加载到CPU寄存器中.这个操作成为上下文切换.Windows实际上保存了一个记录,说明每个线程获得了多少次运行的机会.使用MicrosoftSpy++这个工具可以了解这个情况. 一个线程随时可以停止运行,一个线程可以进行调度.可以对线程进行一定程度的控制,但是不能太多.不能保证一个线程做任何事. 7.1暂停和

回炉重造之重读Windows核心编程-011-线程池和其他异步方式

线程池的使用 多线程应用程序很难设计,有两大难点,一是要管理线程的创建和撤销,再就是要对线程访问资源时实施同步.同步的工具有几个了.为了应对线程频繁地创建和撤销,线程池这个方案被放上了台面.Windows2000提供了一些新的线程池函数,使得线程的创建.撤销和基本管理更加容易.线程池的实现不拘一格,只要遵循以下的要点: 异步调用函数. 按照规定的时间间隔调用函数. 当单个内核对象变为已通知状态时调用函数. 当异步IO请求完成时调用函数. 为了完成这些操作,线程池由4个独立的部分组成.下面shix

【转】《windows核心编程》读书笔记

这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁,往往是很多单独的字句,后面的内容更为连贯. 海量细节. 第1章    错误处理 1.         GetLastError返回的是最后的错误码,即更早的错误码可能被覆盖. 2.         GetLastError可能用于描述成功的原因(CreatEvent)

【windows核心编程】DLL相关(3)

DLL重定向 因为DLL的搜索路径有先后次序,假设有这样的场景:App1.exe使用MyDll1.0.dll, App2.exe使用MyDll2.0.dll, MyDll1.0 和 MyDll2.0是同一个DLL的两个版本,1.0为旧版本,2.0为新版本. 而如果MyDll2.0.dll的存放路径的优先次序比较靠前时,那么App1.exe就会去加载MyDll2.0.dll,这就可能引发 DLL地狱问题,因此DLL重定向可解决这个问题. 加载程序总是先检查应用程序目录,我们所要做的就是如下: ①在

windows核心编程 DLL技术 【转】

注:本文章转载于网络,源地址为:http://blog.csdn.net/ithzhang/article/details/7051558 本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑定的技术. 第一种将DLL映射到进程地址空间的方式是直接在源代码中引用DLL中所包含的函数或是变量,DLL在程序运行后由加载程序隐式的载入,此种方式被称为隐式链接. 第二种方式是在程序运行时,通过调用API显式的载入所需要的DLL,并显式的链接所想要链接的符号.换句话说,程序在运行时,其中的一个线程

C++Windows核心编程读书笔记

转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔记": 关键词:c++windows 核心 编程 读书笔记 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁