windows内存管理

windows内存管理

windows提供3种方式管理内存:

  • 堆:适合管理大量小型对象,方便,但效率低,控制不灵活;
  • 虚拟内存:适合管理大型对象数组,有丰富接口,控制更精确;
  • 文件映射:适合管理大型数据流,以及同一机器上的不同进程间共享数据。

还值得一提的是页交换:当各个进程所使用的内存数量超出物理内存的时候,操作系统还能够将物理内存中暂时用不到的数据交换到硬盘中。

Y
N
Y
Y
N
N
Y
N
试图访问数据
数据是否在内存中
cpu将虚拟地址映射到物理地址
访问数据成功
是否在页交换文件中
是否有闲置页
将数据从页交换文件载入内存闲置区域
Illegal access
内存中寻找可释放页
页中数据是否已经修改
将页面保存到页交换文件

堆内存管理是建立在虚拟内存管理基础之上的。

创建和获取:

  • HeapCreate(),在进程中创建一个堆并返回句柄
  • GetProcessHeap(),获取当前进程中的一个堆的句柄
  • GetProcessHeaps(),获取所有堆的句柄
  • HeapDestroy():销毁由HeapCreate()创建的堆

HeapCreate()的参数有点东西,去看msdn吧。

进程创建时有一个默认堆,不能销毁。可以不创建而直接GetProcessHeap()。

申请和释放:

  • HeapAlloc(),从指定的堆上分配块
  • HeapReAlloc(),改变已经分配好的堆内存块大小
  • HeapFree(),释放上面两个申请的内存

还有用来遍历的三兄弟:

  • CreateToolhelp32Snapshot()
  • Heap32First()
  • Heap32Next()

其它:

  • GetSystemInfo()
  • HeapSize()

SYSTEM_INFO有页大小成员,分配大小一般是页大小的倍数。

int main()
{
	HANDLE hHeap = HeapCreate(0,0,0);
	SYSTEM_INFO si = { 0 };
	
	GetSystemInfo(&si);
	LPVOID pBuf = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 2);
	printf("heap size: 0x%x\n", HeapSize(hHeap, 0, pBuf));

	memcpy(pBuf, "hello\n", 6);
	printf(pBuf);

	HeapFree(hHeap, 0, pBuf);
	HeapDestroy(hHeap);

	system("pause");
	return 0;
}

虚拟内存

在CPU性能过剩的今天,提升内存容量与频率是提高计算机性能的有力手段。对于内存管理,有这样几个条件:

  • 一个进程不能随意访问其它进程内存;
  • 进程需要更多内存时,可以动态分配;
  • 每个进程的内存管理方式一样;
  • 内存需要有属性,如rwe;
  • 程序员无法察觉这些设计意图

于是,windows引出虚拟内存的概念,除了上面的,还有以下特点:

  • 每个进程有4GB虚拟内存空间;
  • 低2GB是用户空间,低2GB用户代码无法访问高2GB系统空间;
  • 虚拟地址到物理地址的映射由os内核完成;
  • 一个进程的虚拟空间只有一部分映射到部分物理内存;
  • 尽量保证不同进程的同一数据,物理内存中只有一份。
  • 物理内存用爆时,将但是不用的数据swap到硬盘中。

具体的虚拟地址到物理地址映射,是使用分段和分页(主要)实现的。

虚拟内存三种状态

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

刚创建的虚拟内存只是逻辑上存在,没有与物理内存建立映射,这时的虚拟内存就是空闲的free或者说未分配的unallocated。

VirtualAlloc()分配某个内存区域(region)的行为叫做预定

对于一个虚拟空间,大部分区域是空闲的,与提交的区域相比就像海洋和陆地。

虚拟内存映射的三种方式

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

虚拟内存的页面属性

  • PAGE_NOACCESS
  • PAGE_READONLY
  • PAGE_READWRITE
  • PAGE_EXECUTE
  • PAGE_EXECUTE_READWRITE
  • PAGE_WRITE_COPY : 写时拷贝 (用户不能使用)

API及代码示例

  • VirtualAlloc():分配、预定一块虚拟内存
  • VirtualAllocEx()

这俩api可以与物理内存建立映射。大小只能是分页大小的整数倍。

系统分配空间时,起始地址是内存分配粒度的整数倍,x86架构下,粒度是64kB(0x10000)。

LPVOID WINAPI VirtualAlloc(
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);


If the lpAddress parameter is NULL, this value is rounded up to the next page boundary.

  • VirtualFree()
  • VirtualFreeEx()
BOOL WINAPI VirtualFree(
  _In_ LPVOID lpAddress,
  _In_ SIZE_T dwSize,
  _In_ DWORD  dwFreeType
);

If the dwFreeType parameter is MEM_RELEASE, this parameter must be 0 (zero).

int main()
{
	LPVOID pVirAlloc = VirtualAlloc(0, 
		0x10000, 
		MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE);

	memcpy(pVirAlloc, "hello", 7);
	printf("%s\n", pVirAlloc);

	VirtualFree(pVirAlloc, 0, MEM_RELEASE);
	
	system("pause");
	return 0;
}

  • VirtualProtect():修改保护属性
  • VirtualProtectEx()

这个api可以实现改写常量区。改写时不会完全按照size参数修改,而是按照4kB分页来改。

第二个参数?????

int main()
{
	char* p = "hello";
	DWORD dwOldProtect;
	
	VirtualProtect(p, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
	p[1] = 'a';
	printf("%s\n", p);
	
	system("pause");
	return 0;
}

  • VirtualLock()
  • VirtualUnlock()
  • ReadProcessMemory() :读取远程进程内存;
  • WriteProcessMemory() :将数据写入远程进程内存
  • VirtualQuery():查询内存状态
  • VirtualQueryEx()

带Ex的就是可以对其它进程操作。

文件映射

文件映射是将文件映射到进程虚拟内存的高效机制,直接建立内核对象,将文件直接映射到虚拟内存,跨过物理内存这一过程,读写文件时直接读写硬盘。内部设计当然贼麻烦。

映射成功的文件可以用视图(View)来引用这段内存,映射对象分为:

  • 命名的
  • 未命名的

命名的映射对象可以跨进程读写。

映射

作用:

  • 操作大文件
  • 进程间共享内存
  • IPC
APIDesc
GetSystemInfo()用于获取分配粒度
CreateFileMapping()创建mapping对象
OpenFileMapping()打开已命名的mapping对象,可跨进程。
MapViewOfFile()将mapping对象文件映射进内存
UnmapViewOfFile()取消文件映射
FlushViewOfFile()将内存映射写回硬盘

获得映射对象后还要记着映射进内存。最后取消映射。

HANDLE WINAPI CreateFileMapping(
  _In_     HANDLE                hFile,
  _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes,
  _In_     DWORD                 flProtect,
  _In_     DWORD                 dwMaximumSizeHigh,
  _In_     DWORD                 dwMaximumSizeLow,  // 映射文件低4字节		0表示映射整个文件
  _In_opt_ LPCTSTR               lpName
);

LPVOID WINAPI MapViewOfFile(
  _In_ HANDLE hFileMappingObject,
  _In_ DWORD  dwDesiredAccess,
  _In_ DWORD  dwFileOffsetHigh,
  _In_ DWORD  dwFileOffsetLow,      //文件低4字节 这个值必须是内存分页的倍数 
  _In_ SIZE_T dwNumberOfBytesToMap  //映射文件的大小	0表示映射所有
);

简单映射

int main()
{
	HANDLE hFile = CreateFile(
		L"..\\Debug\\CPPTest.exe",
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	HANDLE hFileMap = CreateFileMapping(hFile,
		NULL,
		PAGE_READWRITE,
		0,
		0,
		NULL);

	LPVOID pBuf = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);

	printf("%s\n", pBuf);
	//*(DWORD*)pBuf = 0x4d5a;

	UnmapViewOfFile(pBuf);
	CloseHandle(hFile);
	CloseHandle(hFileMap);
	
	system("pause");
	return 0;
}

/*
MZ?
*/

文件共享

创建映射时要记住参数是INVALID_HANDLE_VALUE

创建并写:

int main()
{
	//创建命名的文件映射
	HANDLE hMap = CreateFileMappingW(
		INVALID_HANDLE_VALUE,
		NULL,
		PAGE_READWRITE,
		0,
		16,
		L"file_mapping_test");

	if (NULL == hMap || INVALID_HANDLE_VALUE == hMap)
	{
		return FALSE;
	}
	//创建view,映射到内存
	LPVOID pBuf = MapViewOfFile(
		hMap,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		16);
	if (NULL == pBuf)
	{
		return FALSE;
	}

	wcscpy_s(pBuf, 6, L"hello");

	while (*(PBYTE)pBuf)
		Sleep(500);
	UnmapViewOfFile(pBuf);

	CloseHandle(hMap);
	
	system("pause");
	return 0;
}

读取:

int main()
{
	//创建命名的文件映射
	HANDLE hMap = OpenFileMapping(
		FILE_MAP_ALL_ACCESS,
		FALSE,
		L"file_mapping_test");

	if (NULL == hMap || INVALID_HANDLE_VALUE == hMap)
	{
		return FALSE;
	}
	//创建view,映射到内存
	LPVOID pBuf = MapViewOfFile(
		hMap,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		16);
	if (NULL == pBuf)
	{
		return FALSE;
	}

	wprintf_s(L"%s\n", pBuf);

	*(PBYTE)pBuf = 0;

	UnmapViewOfFile(pBuf);
	CloseHandle(hMap);

	system("pause");
	return 0;
}

遍历内存

需要的api为VirtualQueryEx(),有一个_out_参数为内存结构体。

typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID BaseAddress;
    PVOID AllocationBase;
    DWORD AllocationProtect;
    SIZE_T RegionSize;
    DWORD State;    // MEM_COMMIT, MEM_FREE , MEM_RESERVE 
    DWORD Protect;
    DWORD Type; // MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE 
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

BaseAddress包含于VirtualAlloc()分配的AllocationBase区域。

如果state为

  • MEM_FREEAllocationBase, AllocationProtect, Protect, Type成员无效;
  • MEM_RESERVEProtect无效
#include <stdio.h>
#include <windows.h>



int main()
{
	MEMORY_BASIC_INFORMATION stcMem;
	LPVOID pAddr = 0;
	LPVOID pLastBase = (LPVOID)-1;
	DWORD dwSize = 0;
	DWORD aProtectTypes[] = {
		PAGE_NOACCESS,
		PAGE_READONLY,
		PAGE_READWRITE,
		PAGE_WRITECOPY,
		PAGE_EXECUTE,
		PAGE_EXECUTE_READ,
		PAGE_EXECUTE_READWRITE,
		PAGE_EXECUTE_WRITECOPY,
	};
	char *aProtectDesc[] = {
		"---",
		"r--",
		"rw-",
		"rw-",
		"cow",
		"--e",
		"r-e",
		"rwe",
		"ewcopy"
	};
	int cProtect = _countof(aProtectTypes);
	while (VirtualQuery(pAddr, &stcMem, sizeof(stcMem)) )
	{
		if (pLastBase != stcMem.AllocationBase)
		{
			pLastBase = stcMem.AllocationBase;
			printf("\nAllocationBase: %p  ", stcMem.AllocationBase);
			printf("\t size: 0x%08x", stcMem.RegionSize);
			printf("\t mem state:");
			switch (stcMem.State)
			{
			case MEM_FREE:
				printf("free\n");
				break;
			case MEM_RESERVE:
				printf("reserve\n");
				break;
			case MEM_COMMIT:
				printf("commit\n");
				break;
			default:
				printf("Unknown mem type\n");
				break;
			}
		}
		else
		{
			printf("\t pAddr: %p \n", pAddr);
			printf("\t\t RegionSize: 0x%x\n", stcMem.RegionSize);
			printf("\t\t mem type: ");
			// 查看该内存的类型
			switch (stcMem.Type)
			{
			case MEM_PRIVATE:	printf("  private  \n"); break;
			case MEM_MAPPED:	printf("  mapped  \n"); break;
			case MEM_IMAGE:		printf("  image  \n"); break;
			default:
				printf("Unknown mem type \n");
				break;
			}
			printf("\t\t protect: ");
			for (int i = 0; i < cProtect; ++i)
			{
				if (stcMem.Protect & aProtectTypes[i])
				{
					printf(aProtectDesc[i]);
					printf("\n");
				}
			}
		}

		dwSize = stcMem.RegionSize;

		pAddr = (LPVOID)((ULONGLONG)pAddr + dwSize);
	}
	
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值