操作系統:存儲器管理


存儲器管理

隨着計算機技術的發展,系統軟件和應用軟件在種類、功能上都急劇地膨脹。存儲器容量擴大仍不能滿足現代軟件發展的需要,因此存儲器仍然是一種寶貴而又稀缺的資源。存儲器管理的主要對象是內存,對存儲器的管理直接影響到存儲器的利用率和系統性能也。對外存的管理與對內存的管理相類似,只是外存主要是用來存放文件。

存儲器

在計算機執行時幾乎每一條指令都涉及對存儲器的訪問,因此要求存儲器的速度必須非常快,能與處理機的速度相匹配。此外還要求存儲器具有非常大的容量,且價格還應很便宜。但是實際上魚和熊掌不可兼得,所以在現代計算機系統中都采用了多層結構的存儲器系統。

存儲器的層次結構

對於通用計算機而言,存儲層次至少應具有三級:CPU 寄存器、主存、輔存。實際情況下還可以根據具體的功能細分為寄存器、高速緩存、主存儲器、磁盤緩存、固定磁盤、可移動存儲介質等 6 層。在存儲層次中的層次越高,則存儲介質越靠近 CPU、訪問速度越快,相對的價格也越高且存儲容量也越小。寄存器、高速緩存、主存儲器和磁盤緩存均屬於操作系統存儲管理的管轄范疇,掉電后它們中存儲的信息不再存在。而低層的固定磁盤和可移動存儲介質則屬於設備管理的管轄范疇,存儲的信息將被長期保存。

可執行存儲器

寄存器和主存儲器又被稱為可執行存儲器,進程可以在很少的時鍾周期內使用一條 load 或 store 指令對可執行存儲器進行訪問。對輔存的訪問則需要通過 I/O 設備實現,在訪問中將涉及到中斷、設備驅動程序以及物理設備的運行,輔存的所需耗費的時間遠遠高於訪問可執行存儲器的時間。操作系統的存儲管理負責對可執行存儲器的分配、回收,以及提供在存儲層次間數據移動的管理機制。
寄存器具有與處理機相同的速度,對寄存器的訪問速度最快,但價格昂貴且容量笑。主存儲器簡稱內存或主存,是計算機系統中的主要部件,用於保存進程運行時的程序和數據。通常處理機都是從主存儲器中取得指令和數據的,並將其所取得的指令放入指令寄存器中,所讀取的數據裝入到數據寄存器中,或者將寄存器中的數據存入到主存儲器。

緩存

對於緩存的討論一般涉及高速緩存和磁盤緩存,緩存一般是用於緩和 2 種存介質速度不匹配的問題。

高速緩存

在計算機系統中,為了緩和內存與處理機速度之間的矛盾,許多地方都設置了高速緩存。高速緩存是是介於寄存器和存儲器之間的存儲器,主要用於備份主存中較常用的數據。這樣可以減少處理機對主存儲器的訪問次數,提高程序執行速度。高速緩存容量遠大於寄存器,而比內存約小兩到三個數量級左右,訪問速度快於主存儲器。高速緩存的設計原理是局部性原理,也就是程序在執行時在一較短的時間內,程序的執行僅局限於某個部分。通常進程的程序和數據存放在主存儲器中,每當要訪問時才被臨時復制到速度較快的高速緩存中。當 CPU 訪問一組特定信息時,首先檢查它是否在高速緩存中,如果已存在就直接從中取出使用,否則就從主存中讀出信息。

磁盤緩存

磁盤的 I/O 速度遠低於對主存的訪問速度,為了緩和兩者之間在速度上的不匹配而設置了磁盤緩存。磁盤緩存主要用於暫時存放頻繁使用的一部分磁盤數據和信息,以減少訪問磁盤的次數。但磁盤本身並不是一種實際存在的存儲器,而是利用主存中的部分存儲空間暫時存放從磁盤中讀出(或寫入)的信息。

程序的裝入和鏈接

用戶程序的執行步驟

用戶程序要在系統中運行,須先將它裝入內存然后再將其轉變為一個可以執行的程序。用戶程序的執行通常都要經過以下幾個步驟:

  1. 編譯:由編譯程序對用戶源程序進行編譯,形成若干個目標模塊;
  2. 鏈接:由鏈接程序將若干個目標模塊以及所需要的庫函數鏈接在一起,形成一個完整的裝入模塊;
  3. 裝入:由裝入程序將裝入模塊裝入內存。


程序在編寫的時候使用的是邏輯地址,而最終在內存執行時程序和數據都使用具體的物理地址來存儲,要執行程序就涉及到邏輯地址到邏輯地址的轉換。

程序的裝入

在將一個目標模塊裝入內存時,可以有如下三種裝入方式:

絕對裝入方式

當計算機系統很小且僅能運行單道程序時,完全有可能知道程序在內存物理位置。采用絕對裝入方式(Absolute Loading Mode)是用戶程序經編譯后,產生物理地址的目標代碼。程序中所使用的物理地址可由程序員直接賦予,但要求程序員熟悉內存的使用情況,這樣會提高程序維護的復雜性。因此通常是在程序中采用符號地址,然后在編譯或匯編時轉換為絕對地址。

可重定位裝入方式

在多道程序環境下,編譯程序不可能預知經編譯后所得到的目標模塊應放在內存的何處。因此程序中使用的地址和數據存放的地址都是相對於起始地址而言的邏輯地址,起始地址通常都是從 0 開始的。此時應采用可重定位裝入方式(Relocation Loading Mode),它根據內存的具體情況將裝入模塊裝入到內存的適當位置。
在采用可重定位裝入程序將裝入模塊裝入內存后,會使裝入模塊中的所有邏輯地址與實際裝入內存后的物理地址不同。
例如在用戶程序的 500 號單元處有一條指令 LOAD1,1500,該指令的功能是將 1500 單元中的整數 400 取至寄存器 1。但若直接將該用戶程序裝入到內存的
1000 ~ 3000 號單元,則在執行 1500 號單元中的指令時,仍然會從 1500 號單元中把數據取至寄存器 1,而導致數據錯誤。這是因為用戶程序使用的是邏輯地址,內存內部需要用物理地址來尋址,正確的方法應該將取數指令中的地址 1500 修改成 1500 + 1000 = 2500。

一個作業裝入內存時必須分配作業需要的全部內存空間,如果沒有足夠的內存就不能裝入作業。作業一旦裝入內存后,在運行期間就不能移動,也不能再申請內存空間。

動態運行時的裝入方式

可重定位裝入方式不允許程序運行時在內存中移動位置,因為程序在內存中的移動時必須對程序和數據的絕對地址進行修改。但是在程序運行過程中,它在內存中的位置可能經常要改變。此時可以采用動態運行時裝入(Dynamic Run-time Loading)的方式,裝入程序在把裝入模塊裝入內存后,把這種地址轉換推遲到程序真正要執行時才進行。這種方式的優點是便於程序的修改和更新,實現對目標模塊的共享。因為裝入內存后的所有地址都仍是邏輯地址,這種方式需要一個重定位寄存器的支持。

程序的鏈接

源程序經過編譯后可得到一組目標模塊,鏈接程序的功能是將這組目標模塊以及所需要的庫函數裝配成一個完整的裝入模塊。在對目標模塊進行鏈接時,根據進行鏈接的時間不同可分成如下三種。

靜態鏈接方式

靜態鏈接(Static Linking)方式在程序運行之前,先將各目標模塊及它們所需的庫函數鏈接成一個完整的裝配模塊。進行靜態鏈接時需要對相對地址進行修改,並且變換外部調用符號。
例如經過編譯后所得到的三個目標模塊 A、B、C,長度分別為 L、M 和 N,需要將這幾個目標模塊裝配成一個裝入模塊。在模塊 A 中 有一條語句 CALL B 用於調用模塊 B,在模塊 B 中有一條語句 CALL C,用於調用模塊 C。在由編譯程序所產生的所有目標模塊中使用的都是相對地址,其起始地址都為 0。因為每個模塊中的地址都是相對於起始地址計算的,所以在鏈接后原模塊 B 和 C 在裝入模塊的起始地址要相應的修改為 L 和 L + M。同時也要將每個模塊中所用的外部調用符號也都變換為相對地址,把 B 的起始地址變換為 L,把 C 的起始地址變換為 L + M。

裝入時動態鏈接

裝入時動態鏈接(Load-time Dynamic Linking)是指一組目標模塊在裝入內存時,采用邊裝入邊鏈接的鏈接方式。在裝入一個目標模塊時,若發生一個外部模塊調用事件,將引起裝入程序去找出相應的外部目標模塊並將它裝入內存,修改目標模塊中的相對地址。
裝入時動態鏈接方式便於修改和更新,由於各目標模塊是分開存放的,所以局部的修改並不會影響整個模塊,比較靈活。而靜態鏈接裝配在一起的裝入模塊,如果要修改或更新其中的某個目標模塊,則要求重新打開裝入模塊。同時裝入時動態鏈接便於實現對目標模塊的共享,因為 OS 能輕易將一個目標模塊鏈接到幾個應用模塊上。

運行時動態鏈接

應用程序在運行時,往往每次要運行的模塊可能是不相同的。前 2 種方式由於事先無法知道本次要運行哪些模塊,故只能是將所有可能要運行到的模塊全部都裝入內存並全部鏈接在一起。因此經常有部分目標模塊根本就不運行,所以這么做的效率很低下。
運行時動態鏈接(Run-time Dynamic Linking)鏈接方式是將對某些模塊的鏈接推遲到程序執行時才進行,也就是當發現一個被調用模塊尚未裝入內存時,立即由 OS 去找到該模塊並裝入內存,將其鏈接到調用者模塊上。凡在執行過程中未被用到的目標模塊,都不會被調入內存和被鏈接到裝入模塊上。這樣不僅能加快程序的裝入過程,而且可節省大量的內存空間。

對換

覆蓋技術

早期的計算機內存很小,因此經常會出現內存大小不夠的情況,后來引入了覆蓋技術用來解決“程序大小超過物理內存總和”的問題。覆蓋技術的思想是將程序分為多個段(多個模塊),常用的段常駐內存,不常用的段在需要時調入內存。內存中分為一個“固定區”和若干個“覆蓋區”,需要常駐內存的段放在“固定區”中,調入后就不再調出(除非運行結束)。不常用的段放在“覆蓋區”,需要用到時調入內存,用不到時調出內存。
例如程序 0 是系統中需要頻繁運行的程序,而程序 1 和 2 僅在程序 0 需要完成某些功能的時候調用,且 2 個程序不會同時被調用。這種情況下可以將程序 0 放在固定區中,由於程序 1 和 2 不那么常用且不會同時調用,因此可以准備一個足夠大的覆蓋區來供調用時存放程序。

覆蓋技術必須由程序員聲明覆蓋結構,操作系統完成自動覆蓋。這種方式對用戶不透明,增加了用戶編程負擔,目前已經被淘汰。

對換技術

在內存中的某些進程由於某事件尚未發生而被阻塞運行,但它卻占用了大量的內存空間。另一方面卻又有許多作業因內存空間不足只能駐留在外存上,不能進入內存運行。對換技術是指把內存中暫時不能運行的進程或者暫時不用的程序和數據換出到外存上,騰出足夠的內存空間,再把已具備運行條件的進程或進程所需要的程序和數據換入內存。根據每次對換時所對換的數量,可將對換分為如下兩類:

對換類型 說明
整體對換 以整個進程為單位
頁面(分段)對換 以進程的一個“頁面”或“分段”為單位

對換空間的管理

在具有對換功能的 OS 中,通常把磁盤空間分為文件區和對換區兩部分。文件區占用磁盤空間的大部分,用於存放各類文件。由於通常的文件的訪問的頻率較低,故對文件區管理先是提高文件存儲空間的利用率,然后才是提高對文件的訪問速度,采取的是離散分配方式。對換空間只占用磁盤空間的小部分,用於存放從內存換出的進程。由於進程的對換操作的頻率較高,故對對換空間管理的先是提高進程換入和換出的速度,然后才是提高文件存儲空間的利用率,采取的是連續分配方式。
為了實現對對換區中的空閑盤塊的管理,需要用相應的數據結構記錄外存對換區中的空閑盤塊的使用情況。通常使用空閑分區表空閑分區鏈,在空閑分區表的每個表目中應包含對換區的首址及其大小。由於對換分區的分配采用的是連續分配方式,因而對換空間的分配與回收與動態分區方式時的內存分配與回收方法相同。

進程的換出和換入

當內核發現內存不足時需要調用(或換醒)對換進程,它的主要任務是實現進程的換出和換入。

進程換出

進程換出是將內存中的某些進程調出至對換區,騰出內存空間。首先需要選擇被換出的進程,檢查所有駐留在內存中的進程,首先選擇處於阻塞狀態或睡眠狀態的進程。當有多個這樣的進程時選擇優先級最低的進程換出,為了防止低優先級進程在被調入內存后很快又被換出,還需考慮進程在內存的駐留時間。如果系統中已無阻塞進程但內存空間仍不足,便選擇優先級最低的就緒進程換出。在對進程換出時,只能換出非共享的程序和數據段,對於那些共享的程序和數據段,只要還有進程需要它就不能被換出。在進行換出時應先申請對換空間,若申請成功就啟動磁盤,將該進程的程序和數據傳送到磁盤的對換區上。

進程換入

對換進程將定時執行進程換入操作,首先要先找出“就緒”狀態但已換出的進程。當有許多這樣的進程時,將選擇其中已換出到磁盤上時間最久(必須大於規定時間)的進程作為換入進程,為它申請內存。如果申請成功可直接將進程從外存調入內存,如果失敗則需先將內存中的某些進程換出,騰出足夠的內存空間后再將進程調入。在對換進程成功地換入一個進程后,若還有可換入的進程則再繼續執行換入換出過程,直到內存中再無“就緒且換出”狀態的進程為止。

參考資料

《計算機操作系統(第四版)》,湯小丹 梁紅兵 哲鳳屏 湯子瀛 編著,西安電子科技大學出版社


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM