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 内部调用HeapAlloc,HeapAlloc内部调用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; }