#11樓 得分:0回復於:2006-04-29 08:37:18
GDI資源為什麼會耗盡呢,到哪種程度才表現為耗盡呢? 數據恢復培訓
為了回答這一問題,我們用Windows自帶的任務管理器觀察後發現,當程序界面開始混亂時,進程的GDI對象值為9999,那麼為什麼GDI對象達到9999後界面才發生混亂呢,帶著這個疑問,我查找了一些資料,簡單的了解到了Windows對GDI對象的管理方式。 辦公軟件故障
GDI對象,實際上是Windows系統維護的一些數據結構。微軟基於穩定性和健壯性考慮,將所有GDI對象的管理權都交給Windows系統的對象管理器管理,用戶只能通過系統返回的“句柄”來操作這些對象。 顯卡故障
在Windows 2000中,句柄實際上是一個DWORD類型的值。該DWORD值是一個32比特位的數據,它又分為兩個部分:Table Index及Uniqueness Identifier,他們各占16位,因此,在理論上來說,Windows中的每個進程,所能訪問的GDI對象的最大值是64K。然而,在Windows 2000中,客戶句柄的最大數目被硬設置為16384 (16K);
然而,在Windows 2000中,既便客戶句柄的最大數目被硬設置為16384,那麼為什麼在實際中GDI對象增加到9999後,程序界面就開始混亂了呢?原來,在Windows2000中,每進程的GDI對象的最大值又被默認為10000——據微軟資料顯示,之所以設置為10000,是為了阻止“Bad”程序分配過多的資源,因此,當GDI對象達到9999之後,程序就不能再創建新的GDI資源,這樣,每次都使用新建資源來繪制界面的程序就產生混亂了。
不過,在Windows 2000及以後的操作系統中,每個進程可以創建的GDI對象的最大值,是可以通過修改注冊表來重新設置的,在Windows 2000中,該注冊表項所為:HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下的 "GDIProcessHandleQuota "。設置完新的值後,重啟計算機後,系統中每進程可以使用的GDI對象數就會變成你新設置的數。
然而,上述說法並不完全正確!
據SYBASE的一份資料顯示(?id=1019174),在Windwos2000中,只可以對”GDIProcessHandleQuota”值進行微調,如果設置的值超過15000,系統就變得不穩定。事實上,我在Windows 2000中進行了測試,當GetGuiResources函數的返回值為12288(12K)時,就已經不能創建新的GDI對象了,也就是說,在Windows 2000中當一個進程總的GDI對象數達到12288以後,就不能再創建新的GDI對象了。
那麼,在Windows 2000中,進程所能創建的GDI對象數,是每個進程獨立的,還是要受限於Windows操作系統呢?為此,我寫了一個check程序,該程序批量創建指定數目的BRUSH對象,並統計本進程的GDI對象數及系統中總的GDI對象數,其運行界面如下(這裡圖顯示不出來,不過無關緊要)
其中統計系統中總的GDI對象的代碼如下:
int GetGDINumInSystem(void)
{
int nGDINums = 0; /*所有進程的GDI對象之和*/
int nProcess = 0; /*系統中的進程數*/
DWORD aProID[1024];
DWORD cbNeeded;
::EnumProcesses ( aProID, sizeof(aProID), &cbNeeded );
/*系統中進程總數*/
nProcess = cbNeeded / sizeof ( DWORD );
/*統計每個進程的GDI對象數*/
for ( INT i=0; i < nProcess; i ++ )
{
HANDLE hPro = ::OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProID[i] );
nGDINums += ::GetGuiResources ( hPro, GR_GDIOBJECTS );
CloseHandle ( hPro );
}
return nGDINums;
}
使用此check程序,做如下試驗:
① 修改Windows 2000的注冊表項” GDIProcessHandleQuota”值為12000;
② 開啟一個check進程,在其中創建11000個GDI對象!
③ 開啟第二個check進程(第一個進程不關閉),在其中創建11000個GDI對象。
試驗結果表明,第一個進程的對象可以順利創建,而第二個進程的對象則不能順利創建,這就說明,在Windows 2000中,每個進程可以創建的GDI對象數,不僅在進程內部有一定限制,而且還受限於整個操作系統。經多次試驗表明,當Windows 2000系統中總的GDI對象數達到15900以後的某個值後,進程就不能再創建GDI對象了,系統就變得不穩定了,至於該臨界值到底是多少,各次試驗結果並不一致,但是都在15900以後。
上面的測試是在Windows 2000中進行的,那麼,在Windows 2003中,情況又是什麼樣呢?立即動手,到2003系統中,修改注冊表項”GDIProcessHandleQuota”為20000,然後運行測試程序並在其中創建20000個GDI對象,一切正常! 再改,30000,運行,仍然正常;……直到改到了70000的時候,系統運行才會出現界面繪制問題,在這樣的事實下,不得不做假設:難道Windows 2003中取消了客戶GDI句柄最多16K的限制,而將限制設到了64K?
為了驗證這個推測,我使用check程序直接創建64K的GDI資源,運行結果表明,當進程創建了一定的GDI對象之後,就不能創建新的GDI對象了,這表明上面的推測不完全正確,不過,基於在Windows 2000中的試驗經驗,很快就想到了在Windows 2003中系統可以創建的GDI對象應該還要受限於操作系統,也就是說:Windows 2003中放寬了每個進程可以創建的GDI對象數目,但是整個系統中GDI對象數不能超過某個值。為驗證此結果,做如下試驗:
① 修改Windows 2003的注冊表項” GDIProcessHandleQuota”值為50000;
② 開啟一個check進程,在其中創建40000個GDI對象!
③ 開啟第二個check進程(第一個進程不關閉),在其中創建40000個GDI對象。
試驗結果基本上和Windows 2000中的結果類似,唯一不同的是,在第二個進程創建GDI對象的過程中,當系統中總GDI對象達到63700以後的某個值後才會創建失敗。同樣,該臨界值也不固定,不過多次試驗表明,當總的GDI對象數達到63700以後,系統就變得不穩定了。
結論
經過上面的分析,我們可以知道,在Windows 2000/2003 操作系統中,每個進程可以創建的GDI對象,受限於三個方面因素:系統本身的兩個方面的限制和Windows注冊表中設置的最大值限制。
首先,每個進程可以創建的GDI對象數,在理論上為64K,但是在Windows 2000中,系統將客戶可以創建的GDI句柄數硬設置為不能超過16K,而事實上當GDI對象數達到12K之後,系統即不正常;在Windows 2003中,系統放寬了對GDI對象數的限制,使得每個進程可以使用的GDI對象數接近64K;
其次,每個進程可以創建的GDI對象數,還受限於操作系統中GDI對象總數;在Windows 2000中,當系統中總的GDI對象達到15900以後的某個值後,進程就不能再創建新的GDI對象了;在20003中,這個值增加到63700以後的某個值。但是,到底是哪個值,則不固定。
最後,進程可創建的最大GDI對象數目還受限於注冊表中HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下的 "GDIProcessHandleQuota " 項所設置的值,該值設置了Windows中每個進程可以創建的最大GDI對象數,在Windows 2000及2003系統中,其默認均為10000,這也就說明了當程序創建的GDI對象數達到9999之後界面為什麼會混亂。