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