2013-04-22 149 views
5

我正在編寫一個內核,需要(並且想要)將多個堆棧和堆放到虛擬內存中,但我無法弄清楚如何有效地放置它們。普通程序如何做到這一點?多個堆棧和堆放在虛擬內存中的位置?

如何(或哪裏)將堆棧和堆放入由32位系統提供的有限虛擬內存中,以便它們具有儘可能多的增長空間?

例如,當一個簡單的程序被加載到內存中,其地址空間的佈局可能是這樣的:

[ Code Data BSS Heap-> ... <-Stack ] 

在這種情況下,堆可以成長爲大的虛擬內存允許(例如最多到堆棧),我相信這是這個堆在大多數程序中的工作原理。沒有預定義的上限。

許多程序都具有放置在虛擬地址空間某處的共享庫。 然後有多線程程序有多個堆棧,每個線程一個。而.NET程序有multiple heaps,所有這些都必須能夠以這種或那種方式發展。

我只是沒有看到如何在沒有對所有堆和堆棧的大小進行預定義限制的情況下合理高效地完成此操作。

回答

0

簡而言之,由於您的系統資源總是有限的,所以您不能無限制地使用。

內存管理總是由多個層組成,每個層都有明確的責任。從程序的角度來看,應用程序級管理器是可見的,通常只涉及它自己的單個分配的堆。上面的級別可以處理如果需要從一個全局堆中創建多個堆並將它們分配給子程序(每個都有自己的內存管理器)。以上可能是它使用的標準malloc()/free(),並且高於那些處理頁面和每個進程的實際內存分配的操作系統(它基本上不關心多個堆,而是一般不關心用戶級堆)。

內存管理代價高昂,陷入內核也是如此。將兩者結合起來可能會造成嚴重的性能下降,所以從應用程序的角度來看,實際的堆管理實際上是爲了性能而在用戶空間(C運行時庫)中實現的(以及其他原因超出了現在的範圍)。

當加載一個共享(DLL)庫時,如果它在程序啓動時加載,它當然最有可能加載到CODE/DATA /等,因此不會發生堆碎片。另一方面,如果它是在運行時加載的,那麼除了佔用堆空間幾乎沒有其他可能。 靜態庫當然只是簡單地鏈接到CODE/DATA/BSS/etc部分。

在一天結束時,您需要對堆棧和堆棧施加限制,以避免溢出,但您可以分配其他堆棧。 如果一個人需要成長超出上限,您可以

  • 終止與錯誤的應用程序
  • 有內存管理器分配/調整/移動存儲塊爲堆棧/堆和最有可能整理堆(自己的水平);這就是爲什麼free()通常表現不佳。

考慮每個call爲平均一個相當大的,1KB堆棧幀(如果應用程序開發人員雛可能發生)一個10MB的堆棧就足夠了10240嵌套call -s。順便說一句,除此之外,幾乎不需要每個線程有多個堆棧和堆。

+0

但是在線程之間切換不應該切換整個地址空間(並且導致TLB被刷新),因此對於進程使用的每個線程的每個線程,其堆棧_必須出現在進程的地址空間中。我的文章中的鏈接顯示了一個CLR過程如何有很多堆的圖像。所以這裏需要一個以上的堆棧並堆放在一個地址空間中。 – Virtlink 2013-04-25 14:46:10

+0

地址空間是一個非常不同的抽象層次。實際上,在這種情況下,您在同一個地址空間中有多個堆棧和堆棧。地址空間本身由OS管理,而堆不是;它由用戶級庫代碼管理。 – Powerslave 2013-04-25 15:37:04

2

我假設你已經完成了內核的基礎知識,可以將虛擬內存頁面映射到RAM的頁面錯誤陷阱處理程序。下一級別,您需要一個虛擬內存地址空間管理器,用戶模式代碼可以從中請求地址空間。選擇可防止過度分段的分段粒度,64KB(16頁)是一個很好的數字。允許用戶模式代碼保留空間並提交空間。一個4GB/64KB = 64K x 2位的簡單位圖可以跟蹤段狀態,從而完成工作。頁面錯誤陷阱處理程序還需要查閱此位圖以瞭解頁面請求是否有效。

堆棧是一個固定大小的VM分配,通常爲1兆字節。一個線程通常只需要一小部分頁面,具體取決於函數的嵌套級別,因此請保留1MB並只提交前幾頁。當線程嵌套更深時,它會跳出頁面錯誤,並且內核可以簡單地將額外頁面映射到RAM以允許線程繼續。您需要將底部的幾頁標記爲特殊頁面,當頁面出現故障時,您聲明本網站的名稱。

堆管理器最重要的工作是防止碎片。最好的辦法是創建一個按大小對堆請求進行分區的後備列表。小於8個字節的內容來自第一個分段列表。第二個爲8至16,第三個爲16至32,等等。隨着時間的推移,增加大小桶。您將不得不使用桶尺寸來獲得最佳平衡。非常大的分配直接來自VM地址管理器。

第一次點擊旁視列表中的條目時,您將分配一個新的VM段。您可以使用鏈表將細分細分爲更小的塊。當這樣的分配被釋放時,您將該塊添加到空閒塊列表中。無論程序請求如何,所有塊都具有相同的大小,因此不會有任何碎片。當段被完全使用並且沒有空閒塊可用時,您分配一個新的段。當一個段只包含空閒塊時,您可以將其返回給VM管理器。

該方案允許您創建任意數量的堆棧和堆。

+0

你很好地描述了堆是如何工作的,但我不明白這個「方案」如何讓我創建任何數量的堆。如果我在用戶的代碼和數據後立即爲第一個堆保留16 MB,那麼我該在哪裏放第二個堆?第一個之後?那麼第一個堆不能超過其最初的16 MB。或堆棧擴展(分割堆)的片段,但這對於緩存局部性(例如高頻堆),最大對象大小(例如大對象堆),垃圾收集,或者出於任何原因使用多個堆都是不利的。例如。 .NET有很多堆,他們怎麼做? – Virtlink 2013-04-30 08:57:52

+0

您錯過了堆分配是分段的部分,而不是固定的大小。一個堆在增長時會有很多段,它們可以散佈在整個地址空間中。緩存局部性通常非常好,因爲數組具有來自相同後備列表鏈的固定大小的元素。 – 2013-04-30 09:39:08