系列文章目录
[笔记]Windows核心编程《一》错误处理、字符编码
[笔记]Windows核心编程《二》内核对象
[笔记]Windows核心编程《三》进程
[笔记]Windows核心编程《四》作业
[笔记]快乐的LInux命令行《五》什么是shell
[笔记]Windows核心编程《五》线程基础
[笔记]Windows核心编程《六》线程调度、优先级和关联性
[笔记]Windows核心编程《七》用户模式下的线程同步
[笔记]Windows核心编程《八》用内核对象进行线程同步
[笔记]Windows核心编程《九》同步设备I/O和异步设备I/O
[笔记]Windows核心编程《十一》Windows线程池
[笔记]Windows核心编程《十二》纤程
[笔记]Windows核心编程《十三》windows内存体系结构
[笔记]Windows核心编程《十四》探索虚拟内存
[笔记]Windows核心编程《十五》在应用程序中使用虚拟内存
[笔记]Windows核心编程《十六》线程栈
[笔记]Windows核心编程《十七》内存映射文件
[笔记]Windows核心编程《十八》堆栈
[笔记]Windows核心编程《十九》DLL基础
[笔记]Windows核心编程《二十》DLL的高级操作技术
[笔记]Windows核心编程《二十一》线程本地存储器TLS
[笔记]Windows核心编程《二十二》注入DLL和拦截API
[笔记]Windows核心编程《二十三》结构化异常处理
相关:
文章目录
系列文章目录
前言
一、系统信息
1.1 GetSystemInfo函数
SYSTEM_INFO结构体
1.2 IsWow64Process函数
1.3 系统信息调用示例
二、虚拟内存状态
2.1 GlobalMemoryStatus函数
三、NUMA 机器中的内存状态
3.1 SMP
3.2 NUMA
3.3 GetNumaAvailableMemoryNode函数
示例程序:虚拟内存状态
四、确定地址空间的状态
4.1 VMQuery函数
4.2 示例程序虚拟内存映射
前言
虚拟内存有三种状态:
空闲(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函数
获取当前系统信息。
获取准确的信息对于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的与内存有关的成员
14-2 SystemInfo的与内存无关的成员
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 系统信息调用示例
二、虚拟内存状态
2.1 GlobalMemoryStatus函数
GlobalMemoryStatus可以用来取得当前内存状态的动态信息
WINBASEAPI VOID WINAPI GlobalMemoryStatus( _Out_ LPMEMORYSTATUS lpBuffer );
三、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变量用来返回该节点的内存数量。
示例程序:虚拟内存状态
四、确定地址空间的状态
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;