天天看點

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 内部調用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;
}
           

繼續閱讀