Win32内存管理



                                                                        


  2 地址空间的划分

2.1 用户空间

  地址范围:(0 – 0x7FFFFFFF) 2G,运行应用程序的代码、数据等。


  2.1.1 NULL(空指针区)

    地址范围:(0 – 0x0000FFFF)


  2.1.2 用户区

    地址范围:(0x00010000 – 0x7FFEFFFF)


  2.1.3 64k禁入区(存在于Win XP,Win 7,Win 8 不一定存在)

    地址范围:(0x7FFEFFFF – 0x7FFFFFFF)


2.2 内核空间

  地址范围:(0x80000000 – 0xFFFFFFFF) 2G,运行驱动/内核数据和代码。


地址映射

  1 内存区域

区域指一段连续的地址空间,区域粒度与CPU粒度、操作系统相关。目前,通常以64k粒度存在,地址的对齐方式是以64k为边界。

区域的状态:

1.1 空闲 - 空闲的,可以被使用

1.2 私有已经被占有,但还未使用

1.3 映像程序的代码使用(可理解为代码段)

1.4 映射程序的数据使用(可理解为数据段)


  2 物理内存

可实际使用的物理存储器。


  3 虚拟内存

使用硬盘空间作为内存的拓展,也可当做物理内存使用。


  4 内存页

操作系统使用内存页的方式管理物理内存和虚拟内存。通常情况下,内存页的大小为4k/8k(若分配一个字节大小的内存,其实际分配内存大小也为4k/8k)

每个内存页具有自己的状态(只读/可写/可执行等)


  5 页目表

用于管理内存页的目录表。

页目 -  页表   - 内存页

                内存页

     -  页表   

  -  页表   

指针  31---------22  21--------12 11----------0

        页目       页表     页表偏移量


  6 地址空间的访问

6.1 若地址空间已经存在映射好的物理内存(即对应真正的物理内存),直接返回

6.2 若不存在对应的物理内存,系统去虚拟内存查找对应的内存页。如果未找到,程序报错

6.3 若在虚拟内存中找到对应内存页,系统将虚拟内存切换到物理内存中。返回实际物理内存地址,使用数据


  7 内存的使用

7.1 虚拟内存

  适用于大内存分配。一般情况下,如果分配的内存大于1M,应该使用虚拟内存方式分配内存。


7.2 堆内存

  适用于小内存分配。一般情况下,对于小于1M的内存分配使用。例如malloc/new


7.3 堆栈内存

  系统维护的内存区。


使用虚拟内存

  1 虚拟内存

用于大内存,分配速度快。


  2 虚拟内存的使用

2.1 分配内存(VirtualAlloc)

    LPVOIDVirtualAlloc(

    LPVOIDlpAddress,//NULL或者用于提交的内存地址

    DWORDdwSize,//分配大小,一般是页大小的倍数   DWORD flAlloctionType,//分配方式

    DWORDflProtect);//内存访问方式

MEM_RESERVE方式只预留地址,不分配内存

MEM_COMMIT方式可对预留地址的内存进行分配,也可直接分配内存

  一次分配的最大空间小于用户空间(一般为2G)


2.2 提交内存(VirtualAlloc----MEM_COMMIT)

  pszBuf= (CHAR*)VirtualAlloc(pszBuf,size, MEM_COMMIT, PAGE_READWRITE);


2.3 使用内存

2.4 释放内存(VirtualFree)

  BOOL VirtualFree(

    LPVOIDlpAddress,//释放内存地址

    DWORDdwSize,//释放的大小

    DWORDdwFreeType);//释放方式


  3 内存信息的获取

GlobalMemoryStatus – 获取内存信息的API

Void GLobalMemoryStatus(

LPMEMOEYSTATUS lpBUffer);//获取的内存信息

GlobalMemoryStatusEx – 获取函数增API

Void GLobalMemoryStatusEx(

LPMEMOEYSTATUSEX lpBUffer);//获取的内存信息

 示例代码:

#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#pragma warning(disable:4996);


void MemStatus()//打印内存信息
{
	MEMORYSTATUSEX status = { 0 };
	status.dwLength = sizeof(status);
	GlobalMemoryStatusEx(&status);//获取内存信息

	//打印内存信息
	printf("TotalPhys : %llu\n", status.ullTotalPhys);
	printf("AvailPhys : %llu\n", status.ullAvailPhys);
	printf("TotalPageFile : %llu\n", status.ullTotalPageFile);
	printf("AvailPageFile : %llu\n", status.ullAvailPageFile);
	printf("TotalVirtual : %llu\n", status.ullTotalVirtual);
	printf("AvailVirtual : %llu\n", status.ullAvailVirtual);
	printf("MemoryLoad : %lu\n", status.dwMemoryLoad);


}

void useVirtualMem()//虚拟内存使用
{
	
	MemStatus();
	long long size = 1024 * 1024 * 1024 * sizeof(CHAR);

	//地址分配
	char *pszBuf = (CHAR*)VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);

	printf("MEM_RESERVE : %p\n", pszBuf);

	//内存提交
	pszBuf = (CHAR*)VirtualAlloc(pszBuf, size, MEM_COMMIT, PAGE_READWRITE);

	MemStatus();

	//使用虚拟内存
	printf("MEM_COMMIT: %p\n", pszBuf);
	strcpy(pszBuf, "Hello VirtualMem!");
	printf("%s\n", pszBuf);

	getch();

	//释放内存
	VirtualFree(pszBuf, size, MEM_RELEASE);
}

int main()
{
	useVirtualMem();
	return 0;
}

堆内存

  1 堆内存的特点

分配小数据内存,一般小于1M数据时使用堆内存分配。

一般执行程序后,会有一个默认堆,这个堆一般大小为1M(内存不够时会自动扩展),一个程序可以有多个堆。通过堆内存管理器管理堆内存。

内存分配速度比VirtualAlloc慢。


  2 堆内存的使用

2.1 创建堆

 

HeapCreate

HANDLE  HeapCreate(//返回堆句柄

        DWORD flOptions,//堆得创建标识

        SIZE_T dwInitialSize,//堆得初始化大小

        SIZE_T dwMaximumSize);//堆的最大大小

2.2 分配内存

  HeapAlloc

LPVOID HeapAlloc(//返回内存指针

       HANDLE hHeap,//堆的句柄

       DWORD dwFlags,//分配标识

       SIZE_T dwBytes);//分配大小(字节)

2.3 使用内存

2.4 释放内存

  HeapFree

 BOOL  HeapFree(

        HANDLE hHeap,//堆的句柄

        DWORD dwFlags,//释放标识

 LPVOID lpMem//释放的地址

    );

 

2.5 释放堆

 

HeapDestroy

BOOL  HeapDestroy(

HANDLE hHeap);//堆句柄

 

  3 malloc/VirtualAlloc/HeapAlloc

malloc 内部调用HeapAllocHeapAlloc内部调用VirtualAlloc

malloc 分配内存:

例如100个字节

| 内存头 | 100字节 | 4字节尾部标识|

所有使用malloc分配的内存使用内存头构成链表。


  4 堆的信息

GetProcessHeap() – 当前默认堆的句柄

GetProcessHeaps() – 当前进程所有堆的句柄,返回堆数量

 示例代码:

#include <Windows.h>
#include <stdio.h>

#pragma warning(disable:4996)


void HeapInfo()//打印堆信息
{
	HANDLE hHeap = GetProcessHeap();
	printf("DefaultHeap: %p\n", hHeap);

	HANDLE hHeaps[256] = { 0 };
	int nHeaps = GetProcessHeaps(256, hHeaps);

	printf("AllHeap: %d\n", nHeaps);
	for (int i = 0; i < nHeaps; ++i)
	{
		printf("\t %d  %p\n", i ,hHeaps[i]);
	}
}

void Heap()
{
	HeapInfo();
	//创建堆
	HANDLE hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS,
		                                     1024 * 1024,
											 0);//设置为0时,堆内存自动扩展
	printf("HeapCreate: %p\n", hHeap);

	//内存分配
	CHAR *pszBuf = (CHAR*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 100);

	printf("HeapAlloc: %p\n", pszBuf);

	strcpy(pszBuf, "Hello HeapAlloc!");
	printf("%s\n", pszBuf);
	
	HeapInfo();
	//内存释放
	HeapFree(hHeap, 0, pszBuf);

	//释放堆
	HeapDestroy(hHeap);
	HeapInfo();
}


int main()
{
	Heap();
	return 0;
}

堆栈内存

  堆栈都是小数据的使用,系统维护,栈的大小一般为1M

例如Windows使用 _alloca()函数从栈上分配内存。


内存映射文件(大文件数据的读写)

  1 内存映射文件的含义

可将文件映射为内存,可以像使用内存一样使用文件


  2 内存映射文件的使用

2.1 创建/打开一个文件

  CreateFile()


2.2 创建内存映射文件

  CreateFileMapping()

HANDLE  CreateFileMappingA(

HANDLE hFile, //文件句柄

 LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//     安全属性

 DWORD flProtect,//保护模式

 DWORD dwMaximumSizeHigh,//映射文件大小的高32位

 DWORD dwMaximumSizeLow,//映射文件大小的低32位

 LPCSTR lpName );//文件映射内核对象的名称

2.3 将文件映射为内存地址

 

MapViewOfFile()

LPVOID  MapViewOfFile(

HANDLE hFileMappingObject,//文件映射句柄

DWORD dwDesiredAccess,//访问模式

DWORD dwFileOffsetHigh,//地址偏移高32位

DWORD dwFileOffsetLow,//地址偏移低32位

SIZE_T dwNumberOfBytesToMap);//要映射的字节数

2.4 使用内存

2.5 卸载映射


  UnMapViewOfFile()

BOOL  UnmapViewOfFile(

 LPCVOID lpBaseAddress);

2.6 关闭映射内存文件

  CloseHandle()

2.7 文件的关闭

  CloseHandle()

 代码示例:

#include <Windows.h>
#include <stdio.h>

#pragma warning(disable:4996)

void Map()
{
	//创建文件
	HANDLE hFile = CreateFileA("D:\\map.dat", GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	//创建文件映射
	HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, NULL);

	//映射地址
	CHAR *pszText = (CHAR*)MapViewOfFileEx(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024 * 1024, nullptr);

	//使用内存
	strcpy(pszText, "Hello File Mapping!");
	printf("%s\n", pszText);


	//卸载地址
	UnmapViewOfFile(pszText);
	//关闭文件映射
	CloseHandle(hMap);
	//关闭文件
	CloseHandle(hFile);
}

int main()
{
	Map();
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章