windows内存管理

windows内存管理

1.虚拟内存
我们在程序开发中,需要进行程序调试,或者程序在运行时,都需要接触内存的概念,这里的内存说的都是虚拟内存,不是真实的物理内存。windows采用虚拟内存的技术,使得每个程序运行在自己独立的空间中,进程之间不会相互干扰,程序开发时也只需要程序员管理自己进程的内存,使得开发变得相对简单。虚拟内存又使用分段和分页的机制进行管理。
在虚拟内存的技术可以实现下面的需求:
a) 无论物理内存有多大,每一个虚拟内存进程都有4GB的虚拟内存空间。
b) 4GB空间中,高2GB为系统空间,存储系统内核对象和数据,低2GB空间存储用户的对象和数据。
c) 多个虚拟内存中的数据可以由同一部分的物理内存数据映射而来。即windows尽量保证不同进程的同一份数据,在物理内存中只存储一份。
d) 当各个进程中所使用的内存数量超过物理内存的时候,操作系统能够将物理内存中暂时用不到的数据交换到硬盘中。

虚拟内存和物理内存的映射关系如下:
在这里插入图片描述

2.winndows中的内存管理方式:
Windows提供了以下3种方式管理内存数据

  • 堆:适合用来管理大量的小型对象,使用堆管理方式可以非常方便的帮我们管理所需要的内存空间,而无需去关心一些细节问题,但是堆管理方式的效率较低,堆内存的控制不够灵活
  • 虚拟内存:适合用于管理大型的对象数组或大型的结构数组,使用虚拟内存管理方式有丰富的内存管理接口,可以使我们更加精确的控制内存数据
  • 虚拟内存:适合用于管理大型的对象数组或大型的结构数组,使用虚拟内存管理方式有丰富的内存管理接口,可以使我们更加精确的控制内存数据

3.代码演示堆的使用:

//堆的使用步骤:
// 1. 创建堆对象或者打开默认堆
// 2. 使用堆对象分配堆块
// 3. 使用堆内存
// 4. 释放堆内存
// 5. 销毁堆对象,注意进程的默认堆不能销毁
HANDLE hHeap = HeapCreate(0, 0, 0); 
SYSTEM_INFO si; 
//系统信息 
GetSystemInfo(&si); 
// 获取系统信息 
//在堆上分配3个页面大小的内存 ,并将内存清零
LPVOID lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 3); 
//对堆进行操作 
memcpy(lpMem, "hello", 5); 
printf("%s", lpMem); 
HeapFree(hHeap, 0, lpMem); 
HeapDestroy(hHeap); 
HANDLE hHeap = GetProcessHeap(); 
// 获取默认堆(进程创建时,系统会自动为其创建默认堆) 
SYSTEM_INFO si; 
// 系统信息 
GetSystemInfo(&si); 
// 获取系统信息 
//在堆上分配3个页面大小的内存 
lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 3); 
HeapFree(hHeap, 0, lpMem); 
//系统分配的堆是默认堆不允许释放 
//HeapDestroy( hHeap );

3.1 堆内存使用时涉及的函数总结:

API说明
HeapCreate堆对象创建,返回堆句柄
HeapDestory堆对象销毁
HeapAlloc从指定的堆上分配块
HeapReAlloc重新分配内存,改变已经分配好的堆内存块大小
HeapSize获取指定堆的大小
HeapFree释放HeapAlloc和HeapReAlloc分配的堆块内存
GetProcessHeap获取当前进程中的默认堆,返回一个句柄
GetProcessHaaps获取进程中的所有堆,包括堆的数量和各个堆的句柄
GetSystemInfo获取系统信息

3.2.堆遍历使用到的API:

API说明
CreateToolhelp32Snapshot可以分别创建进程,线程,进程模块,进程堆的快照
Heap32Firtst用来首次调用,获得第一个堆对象的信息
Heap32Next堆遍历中,获取后续堆对象的信息

4.虚拟内存
4.1 虚拟内存常用的函数:
​ 虚拟内存是按照分页大小来管理的,目前一个内存分页是4KB,故而管理内存分页的时候,总是以4KB为单位管理的,管理虚拟内存通常用到下面的函数:

函数说明
VirtualAlloc在本进空间中分配一块虚拟内存
VirtualAllocEx在其他进程空间中分配一块虚拟内存
VirtualFree在本进程空间中释放一块虚拟内存
VirtualFreeEx在其他进程空间中释放一块虚拟内存
VirtualLock将内存锁定,使其不能交换到交换区
VirtualUnlock为内存解锁
VirtualProtect修改本进程中一块虚拟内存的属性
VirtualProtectEx修改其他进程中的内存属属性
ReadProcessMemory读取远程进程中的内存数据
WriteprocessMemory将数据写入远程进程
VirtualQuery查询本进程虚拟内存分布状态
VirtualQueryEx查询其他进程的虚拟内存分布状态

4.2 虚拟内存的三种状态:

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

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

映射方式说明
private进程私有内存,不被其他进程所共享, 一般是堆,栈
mapped从别的内存映射而来,或者说物理内存中的一块数据映射都多个进程的虚拟内存中
image从程序的PE映像映射而来,一般是映像的区段

4.4 虚拟内存的属性
属性按页来管理,一个页4KB,不同的页设置不同的属性,存储不同用途的数据。

虚拟内存的属性描述
PAGE_READONLY只读,只能从这个分页读取数据
PAGE_READWRITE可读可写
PAGE_EXECUTE可执行
PAGE_EXECUTE_READ可读可执行
PAGE_EXECUTE_READWRITE可读可写可执行
PAGE_WRITECOPY写时复制
PAGE_EXCUTE_WRITECOPY写时复制可执行
PAGE_NOACCESS不可访问

4.5 虚拟内存的使用

int main() 
{ 
	//虚拟内存的状态 3种 
	//1. MEM_FREE 空闲-没有使用 
	//2. MEM_RESERVE 保留-占用虚拟地址,但是没有映射到物理地址 
	//3. MEM_COMMIT 提交-映射到物理地址 
	LPVOID lpBuff = VirtualAlloc( 
		0, //预定地址 重什么地址开始申请 
		1, //大小 默认会以一页大小对齐 
		MEM_RESERVE | MEM_COMMIT, //申请并提交,可以一次性操作,也可以分开使用两次VirtualAlloc操作 
		PAGE_READWRITE 
	);
	// 可以在其它进程中申请空间 
	// VirtualAllocEx(hProcess,.....) 
	memcpy(lpBuff, "hello", 10);
    // 释放时参数三可以设置为MEM_DECOMIT表示释放后内存为保留状态,设置为
    // MEM_FREE表示释放后内存处于空闲状态
	VirtualFree(lpBuff,1,MEM_FREE); 
	//静态常量区 可读(不可写) 
	char * pName = (char*)"hello"; 
	DWORD dwProtect; 
	//修改页属性 
	//VirtualProtect( 
	// pName, //要修改地址 
	// 10, //修改字节 
	// PAGE_EXECUTE_READWRITE, //新内存属性 读写执行 
	// &dwProtect); //旧属性 
	// VirtualProtectEx(hprocess) 在指定进程中修改 
	pName[0] = 'x'; 
	printf("%s", pName); 
}

5.内存映射

  • 文件映射是一种将文件内容映射到进程虚拟内存中的技术。
  • 映射成功的文件在内存中可以用视图(view)来应用这段内存,从而达到操作这段内存中对应的文件内容。
  • 映射对象分为命名的和未命名(匿名的)。
  • 文件映射可以让文件操作变得简单,还可以实现在不同的进程中共享数据。

5.1 内存映射使用的API

API说明
GetSystemInfo获取系统信息,用于确定分配粒度
CreateFileMapping创建一个Mapping对象
OpenFileMapping打开一个已经命名的Mapping对象(可跨进程)
MapViewOfFile将mapping对象的文件映射到内存
FlushViewOfFile将映射在内存中的文件写回到硬盘中
UnmapViewOfFile取消文件映射

5.2使用内存映射操作文件

int main() 
{ 
	//操纵一个文件,CreateFile,WriteFile,ReadFile 
	//1. 文件映射 速度快,无需申请空间 
	HANDLE hFile = CreateFile( 
		L"1.txt", 
		GENERIC_WRITE| GENERIC_READ, 
		FILE_SHARE_READ | FILE_SHARE_WRITE, 
		NULL, 
		OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, 
		NULL );
		//2.创建一个内存映射对象 
		HANDLE hFileMap = CreateFileMapping( 
			hFile, NULL, PAGE_READWRITE, 0, 0x10, L"test"); 
			//3.将物理地址映射到虚拟地址 SIZE_T Size = 10; 
			LPVOID pFileBuff = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, 0, 0, Size); 
			//4.操作这段内存,写入数据 
			memcpy(pFileBuff, "hello World--", 11); 
			//5.刷新到文件 
			FlushViewOfFile(pFileBuff, 10); 
			//6.取消映射 
			UnmapViewOfFile(pFileBuff); 
			//7.关闭句柄 
			CloseHandle(hFile); 
			CloseHandle(hFileMap); 
			std::cout << "Hello World!\n"; 
}

5.3 使用内存映射在两个进程中共享数据

进程A

#include<Windows.h> 
int main() 
{ 
	//1.创建文件映射 HANDLE hFileMap = CreateFileMapping(
	INVALID_HANDLE_VALUE, //创建文件映射 -1 
	NULL, //文件安全属性 
	PAGE_READWRITE, //页面属性 
	0, 0x0100, 
	L"View_1");    // 文件映射对象名
	//2.物理地址转换虚拟内存地址 
	LPVOID lpBuff = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 10); 	//3.写入数据 
	memcpy(lpBuff, "hello,world", 10); 
	//4.等待读取 
	system("pause"); 
	//5.释放文件映射 
	UnmapViewOfFile(lpBuff); 
	CloseHandle(hFileMap); 
	std::cout << "Hello World!\n"; 
}

进程B

#include<Windows.h> 
int main() 
{ 
	//1.打开已经命名的文件映射对象"View_1" 
	HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"View_1"); 	
    //2.将物理地址转换虚拟地址 
	LPVOID lPbuff = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS,0,0,10); 	
	 //3.读写数据 
	printf("buff:%s\n", lPbuff); 
	//4. 取消映射关系 
	UnmapViewOfFile(lPbuff); 
	CloseHandle(hFileMap); 
}

6.遍历内存

使用VirtualQuery查询进程虚拟内存的使用情况

使用VirtualQueryEx查询其他进程内存使用的情况

VirtualQueryEx的函数原型如下:

SIZE_T WINAPI VirtualQueryEx(
	_In_ HANDLE hProcess,	// 目标进程句柄
	_In_opt_ LPCVOID lpAddress,		// 查询内存的目标地址
	_Out_ PMEMORY_BASIC_INFORMATION lpBuffer,	// 内存的信息的结构体指针
    _In_ SIZE_T dwLength	// 传出结构体的大小
	);
// 此函数执行后会返回一个MEMORY_BASIC_INFORMATION结构体,里面包含指定查询内存地址的详细信息

// 结构体原型如下:
typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;	// 将参赛向下取整到页面大小
  PVOID  AllocationBase;	// 区域地址,此区域包含传入的地址
  DWORD  AllocationProtect;	// 此区域在预定时的保护属性
  SIZE_T RegionSize;	// 区域的大小
  DWORD  State;	 // 区域的页面状态,MEM_FREE,MEM_RESERVE,MEM_COMMIT
  // 如果页面状态是MEM_FREE,则AllocationBase,AlloctionProtect,State,Protect的值都将无效,
  // 如果页面状态是MEM_RESERVE,则Protect的值无效
  DWORD  Protect;	// 页面保护属性
  DWORD  Type;		// 页面类型MEM_IMAGE,MEM_MAPPED,MEM_PRIVATE
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

使用VirtualQuery查询本进程虚拟内存的使用情况:

#include<Windows.h> 
int main() 
{ 
	//1.遍历进程 内存状态 
	DWORD Addres = 0, Size = 0; 
	//保存内存信息 
	MEMORY_BASIC_INFORMATION Basicinfo = {};
	//遍历进程所有分页,输出内容 
	while (VirtualQuery((LPCVOID)Addres, &Basicinfo, sizeof(MEMORY_BASIC_INFORMATION))) 
	{ 
		Size = Basicinfo.RegionSize; 
		printf("%08p ", Basicinfo.BaseAddress); 
		//当前状态 
		switch (Basicinfo.State) 
		{
		case MEM_FREE: printf("空闲\n"); break; 
		case MEM_RESERVE: printf("保留\n"); break; 
		case MEM_COMMIT: printf("提交\n"); break; 
		default: 
			printf("未知\n"); 
			break; 
		}
		//如果是提交状态的内存区域,那么遍历所有块中的信息 
		if (Basicinfo.State == MEM_COMMIT) 
		{ 
			//遍历所有基址是Address 
			LPVOID BaseBlockAddress = (LPVOID)Addres; DWORD BlockAddress = Addres; 
			DWORD dwBlockSize=0; 
			// 遍历大内存块中的小内存块 
			while (VirtualQuery((LPVOID)BlockAddress, &Basicinfo, sizeof(Basicinfo))) 
			{ 
				if (BaseBlockAddress != Basicinfo.AllocationBase) 
				{ 
					break; 
				}
				printf(" %08X ", BlockAddress); 
				//查看内存状态,映射方式 
				switch (Basicinfo.Type) 
				{
				case MEM_PRIVATE: printf("私有 "); break; 
				case MEM_MAPPED: printf("映射 "); break; 
				case MEM_IMAGE: printf("镜像 "); break; 
				default: printf("未知 "); break; 
				}
				if (Basicinfo.Protect == 0) 
					printf("---"); 
				else if (Basicinfo.Protect & PAGE_EXECUTE) 
					printf("E--"); 
				else if (Basicinfo.Protect & PAGE_EXECUTE_READ) 							
					printf("ER-"); 
				else if (Basicinfo.Protect & PAGE_EXECUTE_READWRITE) 						
					printf("ERW"); 
				else if (Basicinfo.Protect & PAGE_READONLY) 
					printf("-R-");
				else if (Basicinfo.Protect & PAGE_READWRITE) 
					printf("-RW"); 
				else if (Basicinfo.Protect & PAGE_WRITECOPY) 								
					printf("WCOPY"); 
				else if (Basicinfo.Protect & PAGE_EXECUTE_WRITECOPY) 						
					printf("EWCOPY"); 
				printf("\n"); 
				//计算所有相同块大小 
				dwBlockSize += Basicinfo.RegionSize; 
				// 累加内存块的位置 
				BlockAddress += Basicinfo.RegionSize; 
			}
			//内有可能大小位空 
			Size = dwBlockSize? dwBlockSize:Basicinfo.RegionSize; 
		}
		//下一个区域内存信息 
		Addres += Size; 
	} 
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值