《windows 核心编程》 -探索虚拟内存

14.1 系统信息

操作系统中有许多值 是由系统所运行的主机所决定的。如果页面大小和分配粒度等。我们决对不应该在代码中将这些值写死。

此函数得到系统信息VOID GetSystemInfo(LPSYSTEM_INFO ps)

如果想得到机器中与处理器有关的详细信息可以调用GetLogicalProcesorInfomation函数

为了让32位应用程序在64位版本的Windows运行,Microsoft提供了一个称为windows 32 bit On Windows 64 的模拟层又称为WOW。当32 位应用程序通过WOW64运行时,GetSystemInfo的返回值在64位应用程序可能会有所不同。如果想知道进程是否在WOW64运行可以调用下面的函数

BOOL IsWow64Process(HANDLE hProcess PBOOL pbWow64Process);
只有32位程序在WOW64上运行时该布尔值才会被设为TRUE,在这种情况下我们需要调用void GetNativeSystemInfo来取得原来的SYSTEM_INFO结构。

14.1 虚拟内存状态

Windows函数GlobalMemoryStatus可以用来取得当前内存动态信息
如果预计应用程序会在装有4GB的内存的机器上运行,或者页交换文件的大小可能会大于4GB,那么就应该调用GlobalMemoryStatusEx函数。

14.3 NUMA机器中的内存管理

NUMA(Non-Uniform Memory Acess),非统一内存访问机器中的CPU既能访问自己节点的内存,也能访问其它节点的内存。但是,对CPU来说,访问自己节点的内存比方访问外节点的内存要快的多。在默认情况下,当线程调拨物理存储器时,操作系统会尽量用CPu自己节点的内存来支持物理存储器,以提高内存访问的性能。但是,如果没有足够的内存,那么Windows也会使用其它结点的内存来支持物理存储器。

在调用GlobalMemoryStatusEx函数时,在ullAvailPhys参数中返回的值是所有节点可用内存总量。如果要知道某个特定NUMA节点的内存数量,那么可以调用下面的函数

BOOL GetNumaAvailableMemoryNode(UCHAR uNode, //标识节点
        PULONGLONG pulAvailableBytes);  //用来返回该节点可用的内存总量。

只需要调用GetNumaProcessorNode函数就可以得到一个CPU驻留在哪个NUMA 节点中。

可以用GetNumaHighestNodeNumber(PULONG pulHighestNodeNumber);得到系统中节点的总数。

对于任何一个指定的节点来说,他的值价于0和pulHighestNodeNumber参数所指变量值之间。我们可以调用下面的函数来得到驻留在某个节点中的CPU列表;

BOOL GetNumaNodeProcessorMark(UCHAR uNode,
    PULONGLONG pulProcessorMask);

示例:

#include "stdafx.h"
#include <iostream>
#include <windows.h>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    //获取内存状态的动态信息
    MEMORYSTATUS memStatus = {0};
    memStatus.dwLength = sizeof(MEMORYSTATUS);
    GlobalMemoryStatus(&memStatus);

    cout<<"MemLoaded:"<<memStatus.dwMemoryLoad<<endl;  //告诉我们内存管理系统有多忙,它可以是从0~100之间的任何数值
    cout<<"TobalPhys:"<<memStatus.dwTotalPhys<<endl;   //物理内存总量  如果是1G内存 则会小于1G 因为系统在启动过程中会为非页面缓冲池保留一部分内存
    cout<<"AvailPhy :"<<memStatus.dwAvailPhys<<endl;   //有效物理内存

    cout<<"TotalPageSize:"<<memStatus.dwTotalPageFile<<endl; //表示硬盘页交换文件最多能存放多少字节数据
    cout<<"AvailPageSize:"<<memStatus.dwAvailPageFile<<endl; //页交换文件中尚未调拨的字节

    cout<<"TotalVirtual :"<<memStatus.dwTotalVirtual<<endl; //表示地址空间中为各进程私有的那部分的字节数
    cout<<"AvailVirtual :"<<memStatus.dwAvailVirtual<<endl; //与进程相关,GlobalMemoryStatus会把调用进程的地址空间中所有闲置的区域都加起来

    system("pause");
    return 0;
}

没有哪个成员能表示物理存储器的数量。我们把一个进程的地址空间中被保存在内存里的那些页面称为它的工作集
对于一个进来来说我们可能通过GetProcessMemoryInfo来得到正在使用

HANDLE hCurProcess = ::GetCurrentProcess();
    PROCESS_MEMORY_COUNTERS_EX pmc;
    if(!GetProcessMemoryInfo(hCurProcess,(PROCESS_MEMORY_COUNTERS*)&pmc,sizeof(PROCESS_MEMORY_COUNTERS_EX)))
    {
        cout<<"GetProcessMemoryInfo Failed!"<<endl;
    }
    else
    {
        cout<<"WorkingSetSize:"<<pmc.WorkingSetSize<<endl; //进程程序集正在使用的字节数
        cout<<"PeakWorkSetSize:"<<pmc.PeakWorkingSetSize<<endl;//程序集目前曾使用过的内存数量最大值
        cout<<"PrivateUsage:"<<pmc.PrivateUsage<<endl; //应用程序通过new.malloc,VirtualAlloc显示分配的内存
    }

知道进程工作集大小是极其有用的,因为它可以告诉我们一旦程序到达稳定状态会需要多少内存,将应用程序的工作集减少到最小有助于提高程序性能

14.4 确定地址空间状态

下面函数查询与地址空间中内存地址有关的特定信息(比如大小、存储器类型、保护属性等)。

SIZE_T WINAPI VirtualQuery(
  __in_opt  LPCVOID lpAddress,
  __out     PMEMORY_BASIC_INFORMATION lpBuffer,
  __in      SIZE_T dwLength
);

下面函数可以查询另一个进程的与内存地址相关的特定信息

SIZE_T WINAPI VirtualQueryEx(
  __in      HANDLE hProcess,
  __in_opt  LPCVOID lpAddress,
  __out     PMEMORY_BASIC_INFORMATION lpBuffer,
  __in      SIZE_T dwLength
);
MEMORY_BASIC_INFORMATION 成员结构说明
成员 描述
BaseAddress 它的值等于将参数pvAddress向下取整到页面的大小
AllocationBase 标识出区域的基地址,该区域包含参数pvAddress 所指定的地址
AllocationProtect 标识出最开始预订区域时为该区域指定的保护属性
RegionSize 标识出区域大小(字节),区域起始地址为BaseAddress区域中的所有页面拥有相同的保护属性、状态及类型
State 标识出区域中的页面状态
Proctect 针对所有相邻页面
Type 标识出区域中页面类型

 

    //VirtualQuery
    cout<<"-------------------VirtualQuery----------------------"<<endl;
    MEMORY_BASIC_INFORMATION mbi;
    cout<<"Test Addr: "<<0<<endl;
    SIZE_T bufSize = ::VirtualQuery(0,&mbi,sizeof(MEMORY_BASIC_INFORMATION));
    if(bufSize != 0)
        ShowMemBasicInfo(&mbi);
    int nTemp = 1;
    cout<<"Test Addr: "<<"nTemp"<<endl;
    bufSize = ::VirtualQuery(&nTemp,&mbi,sizeof(MEMORY_BASIC_INFORMATION));
    if(bufSize != 0)
        ShowMemBasicInfo(&mbi);

VOID ShowMemBasicInfo(MEMORY_BASIC_INFORMATION * pmbi)
{
    cout<<"BaseAddress:"<<hex<<pmbi->BaseAddress<<endl;
    cout<<"AllocationBase:"<<hex<<pmbi->AllocationBase<<endl;
    cout<<"AllocationProtect:"<<hex<<pmbi->AllocationProtect<<endl;
    cout<<"RegionSize:"<<hex<<pmbi->RegionSize<<endl;
    cout<<"Protect:"<<hex<<pmbi->Protect<<endl;
    cout<<"Type:"<<hex<<pmbi->Type<<endl;
    cout<<"State:"<<hex<<pmbi->State<<endl;
}

 

14.4.1 VMQuery 函数
虽然VirtualQuery函数和PMEMORY_BASIC_INFORMATION能帮我们更加深刻的理解Windows内存管理,但现在我知道,它们提供的信息尚不足以让我们透彻的理解。问题在于PMEMORY_BASIC_INFORMATION 结构并没有返回系统保存在内部的所有信息。如果想了解一些关于某个内存地址的简单信息,那么VirtualQuery就够用了。举个例子,如果想要知道有没有给某个地址调拨物理存储器,或者是否能读取某个内存地址或者能否写入某个内存地址,那么用VirtualQuery正好。但如果想知道某个已预订区域的大小,或者某个区域中的块的数量,或者某个区域是否包含有线程栈,那么光是调用VirtualQuewry是无法得到我们想要的信息的
为了得到更完整的内存信息,作者创建了自己的VMQuery函数:
BOOL VMQuery(HANDLE hProcess,    LPCVOID pvAddress,    PVMQUERY pVMQ);
VMQuery源码见 《windows核心编程》 第394页
 VMQuery 源码下载
时间: 2024-10-27 16:09:05

《windows 核心编程》 -探索虚拟内存的相关文章

[原创/讨论][windows核心编程一外传]关于访问虚拟地址0的方法。

接上一篇 Windows 核心编程研究系列之一(改变进程 PTE) 内容 上一篇观赏地址 :http://community.csdn.net/Expert/topic/5124/5124747.xml?temp=.2832453 当然系统保证不让访问地址0出于一种保护的目的,是防止未初始化的指针读取数据.我说的访问地址0只是出于一种纯实现的目的,也不提倡大家这样做.说白了只是好玩罢了. 大家都知道在 windows 中读取/写入地址0的指令肯定会出错: // 写 0 地址的内容xor edx,

请教windows核心编程 ErrorShow程序例子问题

问题描述 请教windows核心编程 ErrorShow程序例子问题 刚刚学习win32 有个问题TCHAR buffer[100]; case WM_PAINT:{ hdc = BeginPaint (hwnd &ps) ; DWORD systemLocale = MAKELANGID(LANG_NEUTRAL SUBLANG_NEUTRAL); DWORD dwError = 1; HLOCAL hlocal = NULL; BOOL fOk = FormatMessage( FORMAT

018_《Delphi下深入Windows核心编程》

<Delphi下深入Windows核心编程> Delphi 教程 系列书籍 (018) <Delphi下深入Windows核心编程> 网友(邦)整理 EMail: shuaihj@163.com 下载地址: Part1 Part2 Part3     书名: Delphi下深入Windows核心编程 作者: 飞思科技产品研发中心 出版社: 电子工业出版社 书号: 7505384023 出版日期:2003年1月 开本: 787*1092 1/16 页码: 525 版次: 2003年1

Windows 核心编程研究系列之一(-改变进程PTE属性-)[已补完]

  Windows 核心编程研究系列之一 -改 变 进 程 PTE 属性-              这是我研究windows 核心编程的第一篇正式文章,之所以叫核心编程而不叫内核编程,是我觉得从字面上来看核心(core)比内核(kernel)更靠近windows中心,当然只是偶本人的看法的拉.          我们知道在 win NT 中,系统把每个进程的虚拟4G空间分为两大部份,低2G归用户所有,高2G归系统所有.用户不得访问系统的空间,连读都不行,更别说写了!低2G的用户空间也并不是都能

《windows核心编程系列》二谈谈ANSI和Unicode字符集 .

http://blog.csdn.net/ithzhang/article/details/7916732转载请注明出处!! 第二章:字符和字符串处理     使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是指UTF-16.也就是说每个字符编码为两个字节.65535个字符可以表示世界上大部分的语言.为了软件使国际化大家再编程时应该使用unicode字符集.由于原来学过c语言,不习惯

chHANDLE_DLGMSG(windows核心编程)讲解

看完<Windows程序设计>后开始看<windows核心编程>, 结果看第一个案例的时候就很惊人的发现,Jeffery大牛的代码很深奥.乍一看好像没有包含<windows.h>. 看看包含的头文件发现,CmnHdr.h中已经包含了<windows.h>.而CmnHdr.h中的代码更吓人,如果没有讲解,不知道怎么看才好.后来才知道原来书的最后有专门的搭建环境的介绍,基本上全面的讲解了CmnHdr.h的东西.   CmnHdr.h中包含了大牛的很多自己的东西.

《windows核心编程》 在应用程序中使用虚拟内存

  Microsoft Windows 提供了以下三种机制来对内存进行操控: 虚拟内存 最适合用来管理大型对象数组或大型结构数组 内存映射文件 最适合用来管理大型数据流(通常是文件),以及在同一台机器上运行多个进程之间共享数据 堆 最适合管理大量小型对象   本篇只讨论第一种方式 虚拟内存. 15.1 预订地址空间区域 可以使用VirtualAlloc函数来预订进程中的地址空间区域 LPVOID WINAPI VirtualAlloc( __in_opt LPVOID lpAddress, __

《windows核心编程》 17章 内存映射文件

内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以避免直接对文件IO操作和对文件内存进行缓存 进程间通讯 17.1 映射到内存的可执行文件和DLL 当一个线程调用CreateProcess的时候,系统会执行收入步骤: 1.判断exe位置,如果无法找到exe那么不会创建进程,这时会CreateProcess返回FALSE 2.创建一个新的进程内核对象

《windows核心编程》–Windows内存体结构(一)

13.1 进程虚拟地址空间 每个进程都有自己的专有地址空间,对32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000-0xffffffff的任意值.对64位进程来说则可以表示0x0000000000000000 到0xffffffffffffffff之间任一值. 因为每一个进程都有自己的专有地址空间,当进程中的各线程运行时,它们只访问属于该进程的内存.线程即看不到其他进程内容空间,也无法访问它们. 进程A可以在位于它地址空间内的0x12345678地址处存储