windows核心編程--記憶體堆棧

來源:互聯網
上載者:User

對記憶體進行操作的第三個機制是使用堆棧。堆棧可以用來分配許多較小的資料區塊。例如,若要對連結資料表和連結樹進行管理,最好的方法是使用堆棧,堆棧的優點是,可以不考慮分配粒度和頁面邊界之類的問題,集中精力處理手頭的任務。堆棧的缺點是,分配和釋放記憶體塊的速度比其他機制要慢,並且無法直接控制實體儲存體器的提交和回收。
從內部來講,堆棧是保留的地址空間的一個地區。開始時,保留地區中的大多數頁面沒有被提交實體儲存體器。當從堆棧中進行越來越多的記憶體配置時,堆棧管理器將把更多的實體儲存體器提交給堆棧。實體儲存體器總是從系統的頁檔案中分配的,當釋放堆棧中的記憶體塊時,堆棧管理器將收回這些實體儲存體器。

線程的堆棧:

每當建立一個線程時,系統就會為線程的堆棧(每個線程有它自己的堆棧)保留一個堆棧空間地區,並將一些實體儲存體器提交給這個已保留的地區。按照預設設定,系統保留1 MB的地址空間並提交兩個頁面的記憶體。但是,這些預設值是可以修改的,方法是在你連結應用程式時設定M i c r o s o f t的連結程式的/ S TA C K選項:

 

/STACK:reserve[,commit]

當建立一個線程的堆棧時,系統將會保留一個連結程式的/ S TA C K開關指明的地址空間地區。
進程的預設堆棧

當進程初始化時,系統在進程的地址空間中建立一個堆棧。該堆棧稱為進程的預設堆棧。按照預設設定,該堆棧的地址空間地區的大小是1 MB。但是,系統可以擴大進程的預設堆棧,使它大於其預設值。當建立應用程式時,可以使用/ H E A P連結開關,改變堆棧的1 M B預設區域大小。由於D L L沒有與其相關的堆棧,所以當連結D L L時,不應該使用/ H E A P連結開關。/ H E A P連結開關的句法如下:

 

/HEAP:reserve[,commit]

許多Wi n d o w s函數要求進程使用其預設堆棧。特別是widows提供的API。對預設堆棧的訪問是順序進行的。換句話說,系統必須保證在規定的時間內,每次只有一個線程能夠分配和釋放預設堆棧中的記憶體塊。如果兩個線程試圖同時分配預設堆棧中的記憶體塊,那麼只有一個線程能夠分配記憶體塊,另一個線程必須等待第一個線程的記憶體塊分配之後,才能分配它的記憶體塊。一旦第一個線程的記憶體塊分配完,堆棧函數將允許第二個線程分配記憶體塊。這種順序存取方法對速度有一定的影響。如果你的應用程式只有一個線程,並且你想要以最快的速度訪問堆棧,那麼應該建立你自己的獨立的堆棧,不要使用進程的預設堆棧。

單個進程可以同時擁有若干個堆棧。這些堆棧可以在進程的壽命期中建立和撤消。但是,預設堆棧是在進程開始執行之前建立的,並且在進程終止運行時自動被撤消。不能撤消進程的預設堆棧。每個堆棧均用它自己的堆棧控制代碼來標識,用於分配和釋放堆棧中的記憶體塊的所有堆棧函數都需要這個堆棧控制代碼作為其參數。

可以通過調用G e t P r o c e s s H e a p函數擷取你的進程預設堆棧的控制代碼:

 

HANDLE GetProcessHeap();

為什麼要建立輔助堆棧

除了進程的預設堆棧外,可以在進程的地址空間中建立一些輔助堆棧。由於下列原因,你可能想要在自己的應用程式中建立一些輔助堆棧:

• 保護群組件。

• 更加有效地進行記憶體管理。

• 進行本地訪問。

• 減少線程同步的開銷。

• 迅速釋放。

保護群組件
通過建立多個獨立的堆棧,是資料隔離,且相互獨立的操作。
更有效記憶體管理

通過在堆棧中分配同樣大小的對象,就可以更加有效地管理堆棧。就是把大小相同的對象放在一個堆棧中進行分配。
進行本地訪問

每當系統必須在R A M與系統的頁檔案之間進行R A M頁面的交換時,系統的運行效能就會受到很大的影響。如果經常訪問局限於一個小範圍地址的記憶體,那麼系統就不太可能需要在R A M與磁碟之間進行頁面的交換。

所以,在設計應用程式的時候,如果有些資料將被同時訪問,那麼最好把它們分配在互相靠近的位置上。
減少線程同步的開銷

正如下面就要介紹的那樣,按照預設設定,堆棧是順序啟動並執行,這樣,如果多個線程試圖同時訪問堆棧,就不會使資料受到破壞。但是,堆棧函數必須執行額外的代碼,以保證堆棧對線程的安全性。如果要進行大量的堆棧分配操作,那麼執行這些額外的代碼會增加很大的負擔,從而降低你的應用程式的運行效能。當你建立一個新堆棧時,可以告訴系統,只有一個線程將訪問該堆棧,因此額外的代碼將不執行。(就是用多個堆棧來減少同步的效能消耗)
迅速釋放堆棧

最後要說明的是,將專用堆棧用於某些資料結構後,就可以釋放整個堆棧,而不必顯式釋放堆棧中的每個記憶體塊。例如,當Windows Explorer遍曆硬碟的目錄階層時,它必須在記憶體中建立一個樹狀結構。如果你告訴Windows Explorer重新整理它的顯示器,它只需要撤消包含這個樹狀結構的堆棧並且重新運行即可(當然,假定它將專用堆棧用於存放分類樹資訊)。對於許多應用程式來說,這是非常方便的,並且它們也能更快地運行。

如何建立輔助堆棧

你可以在進程中建立輔助堆棧,方法是讓線程調用H e a p C r e a t e函數:

 

HANDLE HeapCreate(   DWORD fdwOptions,   SIZE_T dwInitialSize,   SIZE_T dwMaximumSize);

當試圖從堆棧分配一個記憶體塊時, H e a p A l l o c函數(下面將要介紹)必須執行下列操作:

1) 遍曆分配的和釋放的記憶體塊的連結資料表。

2) 尋找一個空閑記憶體塊的地址。

3) 通過將空閑記憶體塊標記為“已指派”分配新記憶體塊。

4) 將新記憶體塊添加給記憶體塊連結資料表。

從堆棧中分配記憶體塊

若要從堆棧中分配記憶體塊,只需要調用H e a p A l l o c函數:

 

PVOID HeapAlloc(   HANDLE hHeap,   DWORD fdwFlags,   SIZE_T dwBytes);

改變記憶體塊的大小

常常需要改變記憶體塊的大小。有些應用程式開始時分配的記憶體塊比較大,然後,當所有資料放入記憶體塊後,再縮小記憶體塊的大小。有些應用程式開始時分配的記憶體塊比較小,後來需要將更多的資料拷貝到記憶體塊中去時,再設法擴大它的大小。如果要改變記憶體塊的大小,可以調用H e a p R e A l l o c函數:

 

PVOID HeapReAlloc(   HANDLE hHeap,   DWORD fdwFlags,   PVOID pvMem,   SIZE_T dwBytes);

瞭解記憶體塊的大小

當記憶體塊分配後,可以調用H e a p S i z e函數來檢索記憶體塊的實際大小:

 

SIZE_T HeapSize(   HANDLE hHeap,   DWORD fdwFlags,   LPCVOID pvMem);

釋放記憶體塊

當不再需要記憶體塊時,可以調用H e a p F r e e函數將它釋放:

 

BOOL HeapFree(   HANDLE hHeap,   DWORD fdwFlags,   PVOID pvMem);

撤消堆棧

如果應用程式不再需要它建立的堆棧,可以通過調用H e a p D e s t r o y函數將它撤消:

 

BOOL HeapDestroy(HANDLE hHeap);

調用H e a p D e s t r o y函數可以釋放堆棧中包含的所有記憶體塊,也可以將堆棧佔用的實體儲存體器和保留的地址空間地區重新返回給系統。如果該函數運行成功, H e a p D e s t r o y返回T R U E。如果在進程終止運行之前沒有顯式撤消堆棧,那麼系統將為你將它撤消。但是,只有當進程終止運行時,堆棧才能被撤消。如果線程建立了一個堆棧,當線程終止運行時,該堆棧將不會被撤消。

在進程完全終止運行之前,系統不允許進程的預設堆棧被撤消。如果將進程的預設堆棧的控制代碼傳遞給H e a p D e s t r o y函數,系統將忽略對該函數的調用。

由於進程的地址空間中可以存在多個堆棧,因此可以使用G e t P r o c e s s H e a p s函數來擷取現有堆棧的控制代碼:

 

DWORD GetProcessHeaps(   DWORD dwNumHeaps,   PHANDLE pHeaps);

若要調用G e t P r o c e s s H e a p s函數,必須首先分配一個H A N D L E數組,然後調用下面的函數:

 

HANDLE hHeaps[25];DWORD dwHeaps = GetProcessHeaps(25, hHeaps);if(dwHeaps > 5) {   //More heaps are in this process than we expected.} else{   //hHeaps[0] through hHeap[dwHeaps - 1]   //identify the existing heaps.}

注意,當該函數返回時,你的進程的預設堆棧的控制代碼也包含在堆棧控制代碼的數組中。

H e a p Va l i d a t e函數用於驗證堆棧的完整性:

 

BOOL HeapValidate(   HANDLE hHeap,   DWORD fdwFlags,   LPCVOID pvMem);

調用該函數時,通常要傳遞一個堆棧控制代碼,一個值為0的標誌(唯一的另一個合法標誌是H E A P _ N O _ S E R I A L I Z E),並且為p v M e m傳遞N U L L。然後,該函數將遍曆堆棧中的記憶體塊以確保所有記憶體塊都完好無損。為了使該函數運行得更快,可以為參數p v M e m傳遞一個特定的記憶體塊的地址。這樣做可使該函數只檢查單個記憶體塊的有效性。

若要合并地址中的空閑記憶體塊並收回不包含已經分配的地址記憶體塊的儲存空間頁面,可以調用下面的函數:

 

UINT HeapCompact(   HANDLE hHeap,   DWORD fdwFlags);

通常情況下,可以為參數f d w F l a g s傳遞0,但是也可以傳遞H E A P _ N O _ S E R I A L I Z E。

下面兩個函數H e a p L o c k和H e a p U n l o c k是結合在一起使用的:

 

BOOL HeapLock(HANDLE hHeap);BOOL HeapUnlock(HANDLE hHeap);

這些函數是用於線程同步的。當調用H e a p L o c k函數時,調用線程將成為特定堆棧的所有者。如果其他任何線程呼叫堆疊函數(設定相同的堆棧控制代碼),系統將暫停調用線程的運行,並且在堆棧被H e a p U n l o c k函數解鎖之前不允許它醒來。

H e a p A l l o c、H e a p S i z e和H e a p F r e e等函數在內部調用H e a p L o c k和H e a p U n l o c k函數來確保對堆棧的訪問能夠順序進行。自己調用H e a p L o c k或H e a p U n l o c k這種情況是不常見的。

最後一個堆棧函數是H e a p Wa l k:

 

BOOL HeapWalk(   HANDLE hHeap,   PPROCESS_HEAP_ENTRY pHeapEntry);

該函數只用於調試目的。它使你能夠遍曆堆棧的內容。可以多次調用該函數。

zz
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.