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