實驗題目:系統內存使用統計
一、實驗目的
1、瞭解Windows內存管理機制,瞭解頁式存儲管理技術。
2、熟悉Windows內存管理基本數據結構。
3、使用Windows內存管理基本API的使用
二、實驗要求
1、使用Windows系統提供的函數和數據結構顯示系統存儲空間的使用情況。
2、內存和虛擬存儲空間變化時,給出系統顯示變化結果。
3、分析實驗結果。
入坑(全網都是這一個的改編)
錯誤代碼(但大家都在用):
#include "stdio.h"
#include "windows.h"
void MemoryStatus(void) //統計內存的狀態
{
MEMORYSTATUS lpmemory;
GlobalMemoryStatus(&lpmemory);//此函數用來獲得當前可用的物理和虛擬內存信息
printf("物理內存總大小: %dMB\n", lpmemory.dwTotalPhys / (1024 * 1024));
printf("可用物理內存: %dMB\n", lpmemory.dwAvailPhys / (1024 * 1024));
printf("頁文件總大小: %dMB\n", lpmemory.dwTotalPageFile / (1024 * 1024));
printf("可用頁文件大小: %dMB\n", lpmemory.dwAvailPageFile / (1024 * 1024));
printf("虛擬地址空間總大小: %dMB\n", lpmemory.dwTotalVirtual / (1024 * 1024));
printf("可用虛擬地址空間大小:%dMB\n", lpmemory.dwAvailVirtual / (1024 * 1024));
printf("內存加載率: %d%% \n", lpmemory.dwMemoryLoad);
}
int main()
{
LPVOID Virtualbase;
char *string;
printf("================當前內存的使用情況================\n");
MemoryStatus();//獲取當前的內存狀態
printf("\n\n現在開始分配10MB物理內存和100MB虛擬內存\n\n");
string = (char *)malloc(10 * 1024 * 1024); //分配物理內存
Virtualbase = VirtualAlloc(NULL, 100 * 1024 * 1024 , MEM_COMMIT, PAGE_READWRITE); //分配虛擬內存
if (Virtualbase == NULL) printf("虛擬內存分配失敗\n");
printf("================分配完之後的內存狀態如下================\n");
MemoryStatus();
printf("\n\n現在開始分配10MB物理內存和100MB虛擬內存\n\n");
free(string); //釋放物理內存
if (VirtualFree(Virtualbase, 0, MEM_RELEASE)==0) //釋放虛擬內存
printf("釋放虛擬內存失敗!!\n");
printf("================釋放完之後的內存狀態如下================\n");
MemoryStatus();
return 0;
}
運行結果:
結果分析:
(1)對於物理內存,我選擇分配10MB(對於MB分配的時候,這個單位應該是1024*1024),但是分配之後的結果出現了三種:大於、小於、等於10MB的都有;另外,釋放完之後一般是回不到最初的狀態。
(2)對於虛擬內存,分配的是100MB,結果顯示分配的大小總是:分配的虛擬內存+分配的物理內存,釋放完之後都能回到最初的狀態。
這已經出現問題了,完全不符合預期結果?!是的,真的達不到預期!!!
即使從分頁管理方式碎片、用戶行爲不能對內存修改等這樣的角度,總有說不通的地方。
出坑嘗試
從老師那裏得知,malloc申請的是虛擬內存,因此,嘗試將malloc換成new。
new的正確打開方式:
char *string=new char[10 * 1024 * 1024];
釋放new的正確打開方式:
delete string;
觀察運行結果:
事實上,和malloc沒什麼區別。
原來new在Windows中也是在堆中分配,應該也是虛擬內存。
出坑的正確打開方式
最簡單的方法就是:去了解windows系統的內存管理機制
(不過,網上這方面的資料真的不多,原創的更是少)
這裏貼出來查到的一些優質文章:
全面介紹Windows內存管理機制及C++內存分配實例(一):進程空間
全面介紹Windows內存管理機制及C++內存分配實例(二):內存狀態查詢
全面介紹Windows內存管理機制及C++內存分配實例(三):虛擬內存
改後的代碼:
#include<stdio.h>
#include<Windows.h>
#include<iostream>
using namespace std;
//嘗試調用Windows API激活Lock Pages in Memory權限
BOOL LoggedSetLockPagesPrivilege(HANDLE hProcess, BOOL bEnable)
{
struct {
DWORD Count;//數組的個數
LUID_AND_ATTRIBUTES Privilege[1];
} Info;
HANDLE Token;
//打開本進程的權限句柄
BOOL Result = OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES,&Token);
if(Result != TRUE)
{
printf("不能打開進程的權限句柄\n");
return FALSE;
}
else
{
printf("能打開進程的權限句柄\n");
}
//只改變一個屬性
Info.Count = 1;
//準備激活
if (bEnable)
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
else
Info.Privilege[0].Attributes = 0;
//根據權限名字找到LGUID
Result = LookupPrivilegeValue(NULL,SE_LOCK_MEMORY_NAME,&(Info.Privilege[0].Luid));
if (Result != TRUE)
{
printf("無法獲取內存中鎖定頁SE_LOCK_MEMORY_NAME的權限\n");
return FALSE;
}
else
printf("可獲取內存中鎖定頁SE_LOCK_MEMORY_NAME的權限\n");
// 激活Lock Pages in Memory權限
Result = AdjustTokenPrivileges(Token, FALSE, (PTOKEN_PRIVILEGES)&Info, 0, NULL, NULL);
if (Result != TRUE)
{
printf("不能調整進程句柄權限", GetLastError());
return FALSE;
}
else
{
if (GetLastError() != ERROR_SUCCESS)
{
printf("不能使用內存中鎖定頁SE_LOCK_MEMORY_NAME權限\n");
printf("請檢查本地權限\n");
printf("\n");
return FALSE;
}
}
CloseHandle(Token);
return TRUE;
}
//內存和虛擬存儲空間變化情況
void D_F()
{
const int content=100;
MEMORYSTATUS memStatusVirtual;
GlobalMemoryStatus(&memStatusVirtual);
PVOID pVirtual = VirtualAlloc(NULL, content * 1024 * 1024, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
if (pVirtual == NULL)
cout << "沒有那麼大連續進程空間!" << endl;
MEMORYSTATUS memStatusVirtual2;
GlobalMemoryStatus(&memStatusVirtual2);
cout << "=========分配虛擬內存(可用進程空間)=========" << endl;
cout << "減少物理內存=" << memStatusVirtual.dwAvailPhys - memStatusVirtual2.dwAvailPhys << endl;
cout << "減少可用頁文件=" << memStatusVirtual.dwAvailPageFile - memStatusVirtual2.dwAvailPageFile << endl;
cout << "減少可用進程空間="
<< memStatusVirtual.dwAvailVirtual - memStatusVirtual2.dwAvailVirtual << endl;
cout << endl;
if (VirtualFree(pVirtual, 0, MEM_RELEASE) == 0)//釋放虛擬內存
printf("釋放虛擬內存失敗\n");
MEMORYSTATUS memStatusVirtual3;
GlobalMemoryStatus(&memStatusVirtual3);
cout << "=========釋放虛擬內存(可用進程空間)=========" << endl;
cout << "增加可用進程空間="
<< memStatusVirtual3.dwAvailVirtual- memStatusVirtual2.dwAvailVirtual << endl << endl;
cout << endl;
LoggedSetLockPagesPrivilege(GetCurrentProcess(), true);//嘗試調用Windows API激活Lock Pages in Memory權限
//物理內存的變化情況
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
ULONG_PTR pages = (ULONG_PTR)100 * 1024 * 1024 / sysInfo.dwPageSize;
ULONG_PTR *frameArray = new ULONG_PTR[pages];
//如果沒激活權限,是不能調用這個方法的,可以調用,但是返回FALSE
BOOL flag = AllocateUserPhysicalPages(GetCurrentProcess(), &pages, frameArray);
if (flag == false)
{
cout << "=========物理內存分配=========" << endl;
cout << "分配物理內存失敗!" << endl;
cout << endl << endl;
system("pause");
}
else
{
MEMORYSTATUS memStatusVirtual4;
GlobalMemoryStatus(&memStatusVirtual4);
cout << "=========分配物理內存=========" << endl;
cout << "減少物理內存=" << memStatusVirtual3.dwAvailPhys - memStatusVirtual4.dwAvailPhys << endl;
cout << "減少可用頁文件=" << memStatusVirtual3.dwAvailPageFile - memStatusVirtual4.dwAvailPageFile << endl;
cout << "減少可用進程空間=" << memStatusVirtual3.dwAvailVirtual - memStatusVirtual4.dwAvailVirtual << endl << endl;
MEMORYSTATUS memStatusVirtual5;
GlobalMemoryStatus(&memStatusVirtual5);
cout << "=========釋放物理內存=========" << endl;
cout << "增加物理內存=" << memStatusVirtual5.dwAvailPhys - memStatusVirtual4.dwAvailPhys << endl;
}
}
int main()
{
D_F();
return 0;
}
運行結果:
結果分析:
本實驗採取了先對虛擬內存進行分配和釋放,再對物理內存進行分配和釋放的方式,通過Windows系統提供的函數和數據結構顯示觀察了虛擬內存空間和物理內存空間的變化,顯示結果如以上兩圖所示。通過實驗結果分析,具體如下:
(1)對於虛擬內存來說,分配時有時會產生頁目和頁表放在物理內存裏,相應地物理內存會減小(減少的物理內存比較小),釋放分配的虛擬內存時,增加的虛擬內存空間大小=減少的虛擬內存空間大小,符合預期結果。
(2)對於物理內存來說,用戶行爲往往不能改變物理內存的分配和釋放,在本實驗中,對激活物理內存中的鎖定頁進行了嘗試,雖然能打開進程的權限句柄並獲取到內存鎖定頁的權限,但是卻不能使用內存鎖定頁的權限,所以最終無法分配物理內存。
(3)如果想要進一步解決不能使用內存中鎖定頁權限的問題,可以通過查閱資料,檢查本地權限,然後對其嘗試不同的方式進行修改。
(終於可以把自己說服了)
小小總結一下
(1)malloc和new在windows系統中分配的都是虛擬內存。
(2)如果想要分配物理內存,需要調用allocateuserphysicalpages命令。(但是,使用這個API之前,需要激活內存中鎖定頁權限,因爲用戶行爲是不能對物理內存進行修改的)