內存必須容納操作系統和各種用戶進程,因此應該盡可能有效的分配內存的各個部分。
內存通常分為兩個區域,一個用于駐留操作系統,另一個用于用戶進程。操作系統可以位于低內存,也可以位于高內存,影響這一決定的主要因素是中斷向量的位置,由于中斷向量通常位于低內存,因此程序員通常將操作系統頁放在低內存。
內存映射與保護
在說內存分配之前,先說說內存映射與保護的問題,通過采用重定位寄存器和界限地址寄存器,可以實現這種保護。
重定位寄存器含有最小的物理地址值,界限地址寄存器含有邏輯地址的范圍值(例如:重定位=100050, 界限=74500)。有了重定位寄存器和界限地址寄存器,每個邏輯地址必須小于界限地址寄存器。MMU動態的將邏輯地址加上重定位寄存器的值后映射成物理地址,映射后的物理地址再交送內存單元。
當CPU調度器選擇一個進程來執行時,作為上下文切換工作的一部分,調度程序會用正確的值來初始化重定位寄存器和界限地址寄存器。由于CPU所產生的每一地址都需要與寄存器進行核對,所以可以保證操作系統和其他用戶程序和數據不受該進程的運行所影響。
內存分配
現在我們就來討論一下內存分配,最簡單的內存分配方式之一就是將內存分為多個固定大小的分區(parttiion)。每個分區只能容納一個進程,因此,多道程序的程度會受到分區數所限制。如果使用這種多分區方法,當一個分區空閑的時候,可以從輸入隊列中選擇一個進程,以調入到空閑分區,當進程終止時,其分區可以被其他進程所使用。這種方法最初為IBM OS/360 操作系統所使用,不過現在已經不再使用了。
下面來介紹可變分區方案。在可變分區方案里,操作系統有一個表,用于記錄哪些內存可用和哪些內存已經被使用,在一開始,所有的內存都可用于用戶進程,因此可以作為一大塊可用內存,稱為孔(hole)。當有新進程需要內存時,為該進程查找足夠大的孔,如果找到,可以從該進程分配所需的內存,孔內未分配的內存可以下次再用。
隨著進程進入系統,它們將被加入到輸入隊列,操作系統根據所有進程的內存需要和現有可用內存情況來決定哪些進程可以分配內存。當進程分配到空間時,它就裝入內存,并開始競爭CPU,當進程終止的時候,它將釋放內存,該內存可以被操作系統分配給輸入隊列中的其他進程。
在任意的時候,有一組可用孔(塊)大小列表和輸入隊列,操作系統根據調度算法來對輸入隊列進行排序,內存不斷地分配給進程,直到下一個進程的內存需求不能滿足為止,這時沒有足夠大的可用孔來裝入進程。操作系統可以等到有足夠大的空間。或者往下掃描輸入隊列以確定是否有其他內存需求較小的進程可以被滿足。
通常,一組不同大小的孔分散在內存中,當新進程需要內存時,系統為該進程查找足夠大的孔,如果孔太大,那么就分為兩塊:一塊分配新進程,另一塊還回到集合。當進程終止時,它將釋放其內存,該內存將還給孔集合,如果新孔與其他孔相鄰,那么將這些孔合并成大孔,這時,系統可以檢查是否有進程在等待內存空間,新合并的內存空間是否滿足等待進程。
分配方法
上面講的這種方法是通用動態存儲分配問題的一種情況(根據一組空閑孔來分配大小為 n 的請求)。這個問題有許多解決方法,從一組可用孔中選擇一個空閑的最常用方法有首次適應(first-fit)、最佳適應(best-fit)、最差適應(worst-fit)。
- 首席適應:分配第一個足夠大的孔,查找可以從頭開始,也可以從上次首次適應結束時開始,一旦找到足夠大的空閑空間孔,就可以停止。
- 最佳適配: 分配最小的足夠大的孔,必須查找整個列表,除非列表按大小排序,這種方法可以產生最小剩余孔。
- 最差適應: 分配最大的孔,同樣,必須查找整個列表,除非列表按大小排序,這種方法可以產生最大剩余孔,該孔可能比最佳適應方法產生的比較剩余孔更為有用。
碎片
首次適應方法和最佳適應方法都有外部碎片問題,隨著進程的裝入和移出內存,空閑內存被分為小片段,當所有總的可用內存之和可以滿足請求,但并不連續時,這就出現了外部碎片的問題。這個問題可能很嚴重。在最壞的情況下,每兩個進程之間就有空閑塊(或浪費)。如果這些內存塊是一整塊,那么就可以再運行多個進程。
在首次適應和最佳適應之間的選擇可能會影響碎片的量(對一些系統來說首次適應更好,對另一些系統最佳適應更好)。另一個影響因素是從空閑塊的哪端開始分配(頂端還是末端)。不管使用哪種算法,外部碎片始終是個問題。
根據內存空間總的大小和平均進程大小的不同,外部碎片的重要程度頁頁不同,例如,對采用首次適應方法的統計說明。不管使用什么優化,假定有N個可分配塊,那么可能有0.5N個外部為外部碎片。即1/3的內存可能不能使用。這一特性稱為50%規則。
一種解決外部碎片的問題的方法是緊縮(compaction),緊縮的目的是移動內存內容,以便所有空間合并成一整塊。但緊縮并非總是可能的。如果重定位是靜態的,并且在匯編時或裝入市進行的,那么就不能緊縮。緊縮僅在重定位是動態并在運行時可采用。如果地址被動態重定位,可以首先移動程序和數據,然后再根據新基地址的值來改變基地址寄存器,如果能采用緊縮,還需要評估其開銷,最簡單的合并算法是簡丹的將所有進程移動到內存的一端,而將所有的孔移到內存的另一端,以便生成一個大的空閑塊,這種方案開銷較大。
另一種可能解決碎片問題的方法是允許物理地址空間為非連續。這樣只要有物理內存就可以為進程分配,這種方案有兩種互補的實現技術: 分頁和分段,這兩種技術也可以合并。