[笔记]Windows核心编程《十四》探索虚拟内存

简介: [笔记]Windows核心编程《十四》探索虚拟内存

前言

参考

虚拟内存有三种状态:

  • 空闲(free) : 进程不能访问这种页面,此页面还没有被分配
  • 保留(reserve):这个页面被预定了。但是还未与物理内存映射,因此这里也是不能访问的
  • 提交(commit): 内存已经被分配了,并且也与物理存储器映射了,进程已经可以访问这里

虚拟内存映射的三种方式:

  • private : 进程私有内存,不被其他进程所共享, 一般是堆,栈
  • mapped: 从别的内存映射而来
  • image : 从程序的PE映像映射而来,一般是映像的区段 虚拟内存的页面属性

简单使用虚拟内存:

int main()
{
    //MEM_FREE    空闲,没有被使用
    //MEM_RESERVE 保留,没有与物理地址映射
    //MEM_COMMIT  提交,与物理地址进行映射
    //1.申请虚拟内存空间
    LPVOID pBuff = VirtualAlloc(
        0,  //预定的地址,申请空间从这个地址开始
        1,  //申请空间的大小,默认对齐4kb
        MEM_RESERVE | MEM_COMMIT,  //预定并提交
        PAGE_READWRITE           //可写可读
    );
    //VirtualAllocEx 在其它进程中申请空间
    //2.使用它
    memcpy(pBuff, "hello", 6);
    //3.释放内存
    VirtualFree(pBuff, 1, MEM_FREE);
    //VirtualFree 在其它进程中释放空间
    //修改内存分页属性
    char * pName = (char*)"hello";
    DWORD dwOldProtect;
    VirtualProtect(
        pName,          //修改的内存地址
        1,              //修改的大小,会分页对齐
        PAGE_READWRITE, //修改后的新属性
        &dwOldProtect); //修改前的原始属性
    pName[0] = 'x';
    //VirtualProtectEx 修改目标进程内存属性
}

一、系统信息

1.1 GetSystemInfo函数

GetSystemInfo

获取当前系统信息。

获取准确的信息对于WOW64运行的应用程序,调用GetNativeSystemInfo函数。

void WINAPI GetSystemInfo(
  __out  LPSYSTEM_INFO lpSystemInfo
);

启动时系统会确认这些值是多少,因此每个进程只需要调用该函数一次就足够

SYSTEM_INFO结构体

typedef struct _SYSTEM_INFO {
  union {
    DWORD  dwOemId;
    struct {
      WORD wProcessorArchitecture;  处理器体系结构
      WORD wReserved;
    } ;
  } ;
  DWORD     dwPageSize;   //页面大小
  LPVOID    lpMinimumApplicationAddress;  //进程最小可用虚拟机地址
  LPVOID    lpMaximumApplicationAddress; //进程私有空间最大可用内存地址
  DWORD_PTR dwActiveProcessorMask;  //活动CPU掩码
  DWORD     dwNumberOfProcessors;  //CPU数量
  DWORD     dwProcessorType;
  DWORD     dwAllocationGranularity;  //分配粒度
  WORD      wProcessorLevel;
  WORD      wProcessorRevision;
} SYSTEM_INFO

14-1 SystemInfo的与内存有关的成员

成员 描述
dwPageSize 表示CPU页面大小,在x86和x64机器中,该值为4096字节 在IA-64 机器中,该值为8192字节
lpMinimumApplicationAddress 给出每个进程可用地址空间中最小的内存地址,由于每个进程空间的地址空间中最开始64K始终是闲置的,因此该值65536,或0x00001000
lpMaximumApplicationAddress 给出每个进程的私有地址空间中最大的可用内存地址
dwAllocationGranularity 表示用于预定地址空间区域的分配粒度,在写作本书时,该值在windows平台上都是65536

14-2 SystemInfo的与内存无关的成员

成员 描述
wReserved 为今后拓展而保留
dwNumberOfProcessors 表示机器中CPU的数量,注意,在装有双核处理器的机器上,该值为2
dwActiveProcessorMask 一个位掩码,用来表示哪些CPU处于活动状态
dwProcessorType 已经作废
wProcessorArchiteeture 表示处理器的体系结构 比如x86,x64或者IA-64
wProcessLevel 进一步细分处理器的体系结构,比如表示 Intel奔腾Ⅲ或奔腾Ⅳ。如果需要确定CPU支持哪些特性,我们应该调用IsProcessorFeaturePresent函数,而不是使用这个字段
wProcessRevision 进一步对wProcessLevel进行细分

1.2 IsWow64Process函数

为了让32位应用程序也能在64位版本的Windows上运行,Microsoft提供了一个称为Windows 32-bit On Windows64-bit的模拟层,又称为WOW64。

当32位应用通过WOW64运行时,GetSystemInfo返回值和他在64位应用程序中所取得的值可能会有不同。例如在32位下SYSTEM_INFO结构的dwPageSize字段值是4KB而在64位下是8KB。

想要知道程序是否在WOW64上运行,可以调用:

WINBASEAPI
BOOL
WINAPI
IsWow64Process(
    _In_ HANDLE hProcess,
    _Out_ PBOOL Wow64Process
    );

hProcess:要查看的目标进程句柄。

Wow64Process:Wow64Process指向返回值,若运行在WOW64模式下返回TRUE。

也可以使用ShlWApi.h中定义的IsOs并传递OS_WOW6432来判断。返回TRUE 则运行在WOW64下。

1.3 系统信息调用示例

github地址

二、虚拟内存状态

2.1 GlobalMemoryStatus函数

GlobalMemoryStatus可以用来取得当前内存状态的动态信息

WINBASEAPI
VOID
WINAPI
GlobalMemoryStatus(
    _Out_ LPMEMORYSTATUS lpBuffer
    );

三、NUMA 机器中的内存状态

NUMA详情

3.1 SMP

所谓对称多处理器结构,是指服务器中多个CPU对称工作,无主次或从属关系。各CPU共享相同的物理内存,每个 CPU访问内存中的任何地址所需时间是相同的。

SMP服务器的主要特征是共享,系统中所有资源(CPU、内存、I/O等)都是共享的。也正是由于这种特征,导致了SMP服务器的主要问题,那就是它的扩展能力非常有限。对于SMP服务器而言,每一个共享的环节都可能造成SMP服务器扩展时的瓶颈,而最受限制的则是内存。由于每个CPU必须通过相同的内存总线访问相同的内存资源,因此随着CPU数量的增加,内存访问冲突将迅速增加,最终会造成CPU资源的浪费,使

CPU性能的有效性大大降低。

3.2 NUMA

由于SMP在扩展能力上的限制,人们开始探究如何进行有效地扩展从而构建大型系统的技术,NUMA就是这种努力下的结果之一。利用NUMA技术,可以把几十个CPU(甚至上百个CPU)组合在一个服务器内。NUMA服务器的基本特征是具有多个CPU模块,每个CPU模块由多个CPU(如4个)组成,并且具有独立的本地内存、I/O槽口等。由于其节点之间可以通过互联模块(如称为Crossbar

Switch)进行连接和信息交互,因此每个CPU可以访问整个系统的内存(这是NUMA系统与MPP系统的重要差别)。显然,访问本地内存的速度将远远高于访问远地内存(系统内其它节点的内存)的速度,这也是非一致存储访问NUMA的由来。由于这个特点,为了更好地发挥系统性能,开发应用程序时需要尽量减少不同CPU模块之间的信息交互。利用NUMA技术,可以较好地解决原来SMP系统的扩展问题,在一个物理服务器内可以支持上百个CPU。比较典型的NUMA服务器的例子包括HP的Superdome、SUN15K、IBMp690等。

每个CPU模块之间都是通过互联模块进行连接和信息交互,CPU都是互通互联的,同时,每个CPU模块平均划分为若干个Chip(不多于4个),每个Chip都有自己的内存控制器及内存插槽。

非统一内存访问(None-Uniform Memory Access,NUMA): 指机器中的cpu既能访问自己节点的内存也能访问其他节点的内存。

通常为了提高性能,尽量会使用自己节点的内存来支持物理存储器以提高内存访问的性能,但是如果没有足够的内存,windows也会使用外节点的内存来支持物理存储器。

3.3 GetNumaAvailableMemoryNode函数

如果要知道某个特定NUMA节点的内存数量,那么可以调用下面的函数:

WINBASEAPI
BOOL
WINAPI
GetNumaAvailableMemoryNode(
    _In_  UCHAR Node,
    _Out_ PULONGLONG AvailableBytes
    );

Node: 标识节点

AvailableBytes: 指向LONGLONG变量用来返回该节点的内存数量。

示例程序:虚拟内存状态

github地址

四、确定地址空间的状态

4.1 VMQuery函数

VirtualQuery 函数可以用于查询地址空间中的内存地址有关的特定信息(比如大小,存储器类型以及保护属性)。

WINBASEAPI
SIZE_T
WINAPI
VirtualQuery(
    _In_opt_ LPCVOID lpAddress,
    _Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,
    _In_ SIZE_T dwLength
    );

另一个函数允许一个进程来查询另外一个进程的内存信息。

WINBASEAPI
SIZE_T
WINAPI
VirtualQueryEx(
    _In_ HANDLE hProcess,
    _In_opt_ LPCVOID lpAddress,
    _Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,
    _In_ SIZE_T dwLength
    );
typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID BaseAddress;
    PVOID AllocationBase;
    DWORD AllocationProtect;
    SIZE_T RegionSize;
    DWORD State;
    DWORD Protect;
    DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

4.2 示例程序虚拟内存映射

github地址

相关文章
|
1月前
|
缓存 安全 Java
Java并发编程进阶:深入理解Java内存模型
Java并发编程进阶:深入理解Java内存模型
39 0
|
14天前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
|
6天前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
19天前
|
存储 并行计算 算法
CUDA统一内存:简化GPU编程的内存管理
在GPU编程中,内存管理是关键挑战之一。NVIDIA CUDA 6.0引入了统一内存,简化了CPU与GPU之间的数据传输。统一内存允许在单个地址空间内分配可被两者访问的内存,自动迁移数据,从而简化内存管理、提高性能并增强代码可扩展性。本文将详细介绍统一内存的工作原理、优势及其使用方法,帮助开发者更高效地开发CUDA应用程序。
|
1天前
|
API Windows
MASM32编程获取Windows当前桌面主题名
MASM32编程获取Windows当前桌面主题名
|
24天前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
1月前
|
存储 缓存 编译器
Linux源码阅读笔记06-RCU机制和内存优化屏障
Linux源码阅读笔记06-RCU机制和内存优化屏障
|
27天前
|
数据库 Windows
超详细步骤解析:从零开始,手把手教你使用 Visual Studio 打造你的第一个 Windows Forms 应用程序,菜鸟也能轻松上手的编程入门指南来了!
【8月更文挑战第31天】创建你的第一个Windows Forms (WinForms) 应用程序是一个激动人心的过程,尤其适合编程新手。本指南将带你逐步完成一个简单WinForms 应用的开发。首先,在Visual Studio 中创建一个“Windows Forms App (.NET)”项目,命名为“我的第一个WinForms 应用”。接着,在空白窗体中添加一个按钮和一个标签控件,并设置按钮文本为“点击我”。然后,为按钮添加点击事件处理程序`button1_Click`,实现点击按钮后更新标签文本为“你好,你刚刚点击了按钮!”。
96 0
|
1月前
|
编译器 开发工具 C语言
解锁QtCreator跨界神技!Windows下轻松驾驭OpenCV动态库,让你的跨平台开发如虎添翼,秒变视觉编程大师!
【8月更文挑战第4天】QtCreator是一款强大的跨平台IDE,便于创建多平台应用。本教程教你如何在Windows环境下集成OpenCV库至Qt项目。首先,下载匹配MinGW的OpenCV预编译版并解压。接着,在QtCreator中新建或打开项目,并在.pro文件中添加OpenCV的头文件和库文件路径。确保编译器设置正确。随后编写测试代码,例如加载和显示图片,并进行编译运行。完成这些步骤后,你就能在QtCreator中利用OpenCV进行图像处理开发了。
127 6
|
1月前
|
存储 编译器 Linux
Windows 32 汇编笔记(二):使用 MASM
Windows 32 汇编笔记(二):使用 MASM

热门文章

最新文章