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;
}