《windows 核心编程》 -探索虚拟内存-阿里云开发者社区

开发者社区> 技术小阿哥> 正文

《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 源码下载 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
怎么设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程
8430 0
为什么 Node.js 启动后虚拟内存很大
问题来源 问题来源于cnode社区:node启动占用内存的问题。 自己本地跑了一下,乖乖不得了,启动一个 node,什么都不做。结果是这样子的:果然900+M。 我的电脑信息: $cat /proc/version Linux version 4.
12967 0
虚拟机内存中数据细节
虚拟机内存中数据细节 如何创建? 如何布局? 如何访问?   如何创建? 语言层面:创建对象仅仅是一个new关键字而已。 虚拟机中:   1 检查指令的参数是否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否被加载,解析,初始化;   2 虚拟机为新生对象分配内存. 如何布局? 分为3个区域:   对象头   实例数据   对其填充   对象头: 分为2部分:   1、用于存储对象自身的运行时数据(hashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程的id,偏向时间戳等)。
659 0
13694
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载