操作系统概念学习笔记 15 内存管理(一)
操作系統概念學習筆記 15
內存管理(一)
背景
內存是現代計算機運行的中心。內存有非常大一組字或字節組成,每一個字或字節都有它們自己的地址。CPU依據程序計數器(PC)的值從內存中提取指令。這些指令可能會引起進一步對特定內存地址的讀取和寫入。
一個典型指令運行周期,首先從內存中讀取指令。
接著該指令被解碼,且可能須要從內存中讀取操作數。在指令對操作數運行后,其結果可能被存回到內存。
內存單元僅僅看到地址流,而并不直到這些地址是怎樣產生的(由指令計數器、索引、間接尋址、實地址等)或它們是什么地址(指令或數據)。
基本硬件:
CPU所能直接訪問的存儲器僅僅有內存和處理器內的寄存器。機器指令能夠用內存地址作為參數。而不能用磁盤地址作為參數。假設數據不在內存中,那么CPU使用前必須先把數據移到內存中。
CPU內置寄存器通常能夠在一個CPU時鐘周期內完畢訪問。
對于寄存器的內容,絕大多數CPU能夠在一個時鐘周期內解析并運行一個或多個指令,而對于內存就不行。
完畢內存訪問須要多個CPU時鐘周期。因為沒有數據以便完畢正在運行的指令,CPU通常須要暫停(stall)。因為內存訪問頻繁,這樣的情況是難以忍受的。解決方法是在CPU與內存之間添加快速內存。這樣的協調速度差異的內存緩沖去,稱為快速緩存(cache)。
除了保證訪問物理內存的相對速度之外,還要確保操作系統不會被用戶進程所訪問,以及確保用戶進程不會被其它用戶進程訪問。
當中一種可能方案為:
首先確保每一個進程都有獨立的內存空間,為此,須要確定進程可訪問的合法地址的范圍,并確保進程僅僅能訪問其合法地址。通過基地址寄存器(base register)和界限地址寄存器(limit register)能夠實現這樣的保護。基地址寄存器(base register)含有最小的物理內存地址,界限地址寄存器(limit register)決定了范圍的大小。
比如:假設基地址寄存器為300040而界限寄存器為120900,那么程序能夠訪問從300040到420940的全部地址。
內存空間保護的實現。是通過CPU硬件對用戶模式所產生的每一個地址與寄存器的地址進程比較來完畢的。假設訪問了不該訪問的地址。則會陷入到操作系統中。并作為致命錯誤處理。
操作系統在內核模式下,能夠無限制地訪問操作系統和用戶內存。
因此操作系統能夠將用戶程序裝入用戶內存,在出錯時輸出這些程序,訪問并改動系統調用的參數等。
地址綁定:
通常,程序以二進制可運行文件的形式存儲在磁盤上。為了運行,程序被調入內存并放入進程空間內。
依據所使用的內存管理方案,進程在運行時。能夠在磁盤和內存之間移動。在磁盤上等待調入內存以便運行的進程形成輸入隊列(input queue)。
通常的步驟是從輸入隊列中選取一個進程并裝入內存。進程在運行時。會訪問內存中的指令和數據。最后。進程終止,其地址空間將被釋放。
很多系統同意用戶進程放在物理地址的任何位置。這樣的組合方式會影響用戶程序能夠使用的地址空間。在絕大多數情況下。用戶程序在運行前。會經過好幾個步驟,在這些步驟中,地址可能有不同的表示形式,源程序中的地址一般是用符號(如count)來表示,編譯器通常將這些符號地址綁定(bind)在可重定位的地址(如:從本模塊開始的第14字節)。鏈接程序或載入程序再將這些可重定位的地址綁定成絕對地址(如74014)。每次綁定都是從一個地址空間到還有一地址空間的映射。
通常。將指令與數據綁定到內存地址有下面幾種情況:
編譯時(compile time):假設編譯時就知道進程將在內存中的駐留地址。那么就能夠生成絕對代碼(absolute code)。假設將來開始地址發生變化,那么就必須又一次編譯代碼。
載入時(load time):當編譯時不知道進程將駐留在內存的什么地方,那么編譯器就必須生成可重定位代碼(reloadable code)。綁定會延遲到載入時才進行。假設開始地址發生變化。僅僅須要又一次載入用戶代碼已引入改變值。
運行時(execution time):假設進程在運行時能夠從一個內存段移到還有一個內存段。那么綁定必須延遲到運行時才發生。
絕大多數通用計算機操作系統採用這樣的方法。
邏輯地址空間與物理地址空間:
CPU生成的地址通常稱為邏輯地址(logical address)。而內存單元所示地址(即載入到內存地址寄存器(memory-address register)中的地址)通常稱為物理地址(physical address)。
編譯和載入時的地址綁定方法生成相同的邏輯地址和物理地址。可是。運行時的地址綁定方案導致不同的邏輯地址和物理地址。對于這樣的情況,通常稱邏輯地址為虛擬地址(virtual address)。
由程序所生成的全部邏輯地址稱為邏輯地址空間(logical address space)。與這些邏輯地址相相應的物理地址的集合稱為物理地址空間(physical address space)。
運行時從虛擬地址到物理地址的映射由被稱為內存管理單元(memory-management unit,MMU)的硬件設備來完畢。有非常多可選擇的方法來完畢這樣的映射,如使用一個簡單的MMU方案來實現這樣的映射,這是一種基地址寄存器方案的推廣。基地址寄存器在這里稱為重定位寄存器(relocation register),用戶進程所生成的地址在送交內存之前。都加上重定位寄存器的值。
假如,基地址為14000,那么用戶對地址346的訪問將映射為地址14346。
用戶程序絕對不會看到真正的物理地址。如,程序能夠創建一個指向位置346的指針,將他保存在內存中。使用它,與其它地址進行比較等等,全部這些操作都是基于346進行的。用戶程序處理邏輯地址時。內存映射硬件將邏輯地址轉變為物理地址。所引用的內存地址僅僅有在引用時才最后定位。
邏輯地址空間綁定到單獨的一套物理地址空間。
動態載入(dynamic loading):
一個進程的整個程序和數據假設都必須處于物理內存中,則進程的大小受物理內存大小的限制。
為了獲得更好的內存空間使用率。使用動態載入(dynamic loading),即一個子程序僅僅有在調用時才被載入。
全部的子程序都以可重定位的形式保存在磁盤上。
主程序裝入內存并運行。當一個子程序須要調用另外一個子程序的時候,調用子程序首先檢查還有一個子程序是否已經被載入。假設沒有,可重定位的鏈接程序將用來載入所須要的子程序,并更新程序的地址表以反應這一變化。接著控制傳遞給新載入的子程序。
動態載入的長處是不用子程序絕不會被載入。假設大多數代碼須要用來處理異常情況。如錯誤處理。那么這樣的方法特別實用。對于這樣的情況,盡管整體上程序比較大,可是所使用的部分可能小非常多。
動態載入不須要操作系統提供特別的支持。利用這樣的方法來設計程序主要是用戶的責任。
動態鏈接(dynamically linking)與共享庫:
有的操作系統僅僅支持* 靜態鏈接(static linking)*此時系統語言庫的處理與其它目標模塊一樣,由載入程序合并到二進制程序鏡像中。
動態鏈接的概念與動態載入類似。僅僅是這里不是將載入延遲到運行時。而是將鏈接延遲到運行時。這一特點通經常使用于系統庫。如語言子程序庫。沒有這一點,系統上的全部程序都須要一份語言庫的副本,這一需求浪費了磁盤空間和內存空間。
假設有動態鏈接,二進制鏡像中每一個庫程序的應用都有一個存根(stub)。
存根是一小段代碼,用以指出怎樣定位適當的內存駐留的庫程序。或假設該程序不在內存中應怎樣安裝入庫。無論怎樣,存根會用子程序地址來取代自己,并開始運行子程序。因此,下次再運行該子程序代碼時,就能夠直接進行。而不會因動態鏈接產生不論什么開銷。採用這樣的方案,使用語言庫的全部進程僅僅須要一個庫代碼副本就能夠了。
動態連接也可用于庫更新。一個庫能夠被新的版本號所替代,且使用該庫的全部程序會自己主動使用新的版本號。
沒有動態鏈接。全部這些程序必須又一次鏈接以便訪問。
為了不使程序錯用新的、不兼容版本號的庫。程序和庫將包含版本號信息。多個版本號的庫都能夠裝入內存。程序通過版本號信息來確定使用哪個庫副本。
因此,僅僅實用新庫編譯的程序才會收到新庫的不兼容變化影響。在新程序裝入之前所鏈接的其它程序能夠繼續使用老庫。這樣的系統也稱為共享庫。
與動態載入不同,動態鏈接通常須要操作系統幫助。假設內存中的進程是彼此保護的。那么僅僅有操作系統才干夠檢查所需子程序是否在其它進程內存空間內,或是同意多個進程訪問同一內存地址。
交換
進程須要在內存中以便運行。進程也能夠臨時從內存中交換(swap)到備份存儲(backing store)上,當須要再次運行時在調回到內存中。
在交換策略的變種被用在基于優先級的調度算法中。
假設有一個更高優先級的進程且須要服務,內存管理器能夠交換出低優先級的進程,以便裝入和運行更高優先級的的進程。當高優先級進程運行完后,低優先級進程能夠交換回內存繼續運行。這樣的交換有時候稱為滾入(roll in/swap in)和滾出(roll out/swap out)。
通常,一個交換出的進程須要交換回它原來所占有的內存空間。這一限制是由地址綁定方式決定的。假設綁定是在匯編時或載入時所定的,那么就不能夠移動到不同的位置。假設綁定在運行時才確定,因為物理地址是在運行時才確定。那么進程能夠移動到不同的地址空間。
交換須要備份存儲。
備份存儲一般是快速磁盤。
這必須足夠大。以便容納全部不同用戶的內存鏡像副本,它也必須提供對這些內存鏡像的直接訪問。
系統有一個就緒隊列。它包含在備份存儲或內存中等待運行的全部進程。
當CPU調度程序決定運行進程時,它調用調度程序。
調度程序檢查隊列中的下一進程是否在內存中,假設不在內存中且沒有空暇內存空間。調度程序講一個已在內存中的進程交換出去,并換入所須要的進程。然后。它又一次裝載寄存器,并將控制轉交給所選擇的進程。
交換系統的上下文切換時間比較長。為了有效使用CPU,須要使每一個進程的運行時間比交換時間長。
注意交換時間主要部分是轉移時間。總的轉移時間與所交換的內存空間總量成正比。因此假設僅僅需交換真正使用的內存。便能夠降低交換時間。
為有效使用這樣的方法,用戶須要告訴系統其內存需求情況。因此。具有動態內存需求的進程要通過系統調用(請求內存和釋放內存)來通知操作系統其內存需求變化情況。
交換也受其它因素限制。
假設要換進的進程,那么必須確保該進程全然處于空暇狀態。
假設I/O異步訪問用戶內存的I/O緩沖區,那么該進程就不能被換出。假定因為設備忙,I/O操作在排隊等待。假設換出進程P1換入進程P2。那么I/O操作可能試圖使用如今已經屬于進程P2的內存。
對于這個問題有兩種解決方法:
一是,不能換出有待處理I/O的進程。
二是,I/O操作的運行僅僅能使用操作系統緩沖區。僅當換入進程后。才運行操作系統緩沖與進程內存之間的數據轉移。
交換空間通常作為磁盤的一整塊,且獨立與文件系統。因此使用就可能非常快。
通常并不運行交換,但當有很多進程運行且內存空間吃緊時,交換開始啟動。假設系統負荷降低,交換就暫停。
連續內存分配(contiguous memory allocation)
內存必須容納操作系統和各種用戶進程,因此應該盡可能有效地分配內存的各個部分。
內存通常分為兩個區域:一個用于駐留操作系統,一個用于用戶進程。操作系統能夠位于低內存或高內存,影響這一決定的主要因素是中斷向量的位置。因為中斷向量通常位于低內存,因此程序猿通常將操作系統放到低內存。
通常須要將多個進程同一時候放入內存中。因此須要考慮怎樣為輸入隊列中須要調入內存的進程分配內存空間。
採用連續內存分配(contiguous memory allocation)時,每一個進程位于一個連續的內存區域。
內存映射與保護
通過採用重定位寄存器和界限地址寄存器能夠實現保護。
重定位寄存器含有最小的物理地址值;界限地址寄存器含有邏輯地址的范圍值。
這樣每一個邏輯地址必須小于界限地址寄存器。MMU動態第將邏輯地址加上重定位寄存器的值后影射成物理地址。
映射后的物理地址再送交內存單元。
當CPU調度器選擇一個進程來運行時,作為上下文切換工作的一個部分,調度程序會用正確的值來初始化重定位寄存器和界限地址寄存器,因為CPU所產生的每一地址都須要與寄存器進程核對,所以能夠保證操作系統和其它用戶程序和數據不受該進程運行所影響。
重定位寄存器機制為同意操作系統動態改變提供了一個有效方法。如某驅動程序(或其它操作系統服務)不常使用便能夠不必在內存中,這類代碼有時稱為臨時(transient)操作系統代碼,它們依據須要調入或調出。因此,使用這樣的代碼能夠在程序運行時動態改變操作系統的大小。
內存分配
最簡單的內存分配方法之中的一個是將內存分為多個固定大小的分區(partition)。
每一個分區僅僅能容納一個進程。那么多道程序的程度會受分區數限制。假設使用這樣的多分區方法(multiple-partition method),當一個分區空暇時,能夠輸入隊列中選擇一個進程,以調入到空暇分區。當進程終止時,其分區能夠被其它進程所使用。這樣的方法如今已不再使用。對于固定分區方案的推廣(稱為MVT),它主要用于批處理環境。也可用于純分段內存管理的分時操作系統。
在可變分區(variable-partition)方案中,操作系統有一個表。用于記錄那些內存可用和哪些內存已被占用。
一開始,全部內存都可用于用戶進程,因此能夠作為一大塊可用內存。稱為孔(hole),當新進程須要內存時,為該進程查找足夠大的孔,假設找到,能夠從該孔進程分配所需的內存,孔內未分配的內存可用于下次再用。
隨著進程進入系統,它們將被添加輸入隊列中。
操作系統依據調度算法來對輸入隊列進行排序。
內存不斷地分配給進程,直到下一個進程的內存需求不能滿足為止,假設沒有足夠大的孔來裝入進程,操作系統能夠等到有足夠大的空間。或者往下掃描輸入隊列以確定是否其它內存需求較小的進程能夠被滿足。
通常,一組不同大小的孔分散在內存中。
當新進程須要內存時,系統為進程查找足夠大的孔。假設孔太大。那么就分成兩塊:一塊分配給新進程,還有一塊還回到孔集合,當進程終止時。它將釋放其內存,改內存將還給孔集合。假設孔與其它孔相鄰。那么將這些孔合并為大孔。這時,系統能夠檢查是否有進程在等待內存空間。新合并的內存空間是否滿足等待進程。
這樣的方法是通用動態存儲分配問題的一種情況(依據一組空暇孔來分配大小為n的請求)。這個問題有很多解決方法。
從一組可用孔中選擇一個空暇孔的最為經常用法有首次適應(first-fit)(第一個對夠大的孔)、最佳適應(best-fit)(最小的最夠大的孔)、最差適應(worst-fit)(分配最大的孔)。
首次適應(first-fit):分配第一個足夠大的孔,查找能夠從頭開始。也能夠從上次首次適應結束時開始。一旦找到足夠大的空暇孔,就能夠停止。
最佳適應(best-fit):分配最小的足夠大的孔。
必須查找整個列表,除非列表依照大小排序。這樣的方法能夠產生最小剩余孔。
最差適應(worst-fit):分配最大的孔,相同必須查找整個列表。除非列表依照大小排序。
這樣的方法能夠產生最大剩余孔。
該孔可能比最佳適應方法產生的最小剩余孔更實用。
模擬結果顯示:首次適應和最佳適應方法在運行時間和利用空間方面都好于最差適應方法。
首次適應和最佳適應方法在利用空間方面難分伯仲。首次適應方法更快些。
碎片(fragmentation)
首次適應和最佳適應算法都有外部碎片問題(external fragmentation)。隨著進程裝入和移出內存。空暇內存空間被切割為小分段,
當全部總的空用內存之和能夠滿足請求,但并不連續時,這就出現了外部碎片問題。
最壞的情況下。每兩個進程之間就有空暇塊(或浪費)。假設這些內存是一整塊。那么就能夠再運行多個進程。
在首次適應和最佳適應之間的選擇可能會影響碎片的量。還有一個影響因素是從空暇塊的哪端開始分配。無論使用哪種算法。外部碎片始終是個問題。
依據內存的總大小和平均進程大小的不同。外部碎片化的重要程度也不同。
比如。對採用首次適應方法的統計說明,對于首次適應方法無論怎么優化,假定N個可分配塊。那么可能有0.5N個塊為外部碎片。即1/3內存可能不能使用,這一特性稱為50%規則。
內存碎片能夠是內部的。也能夠是外部的。假設內存以固定大小的塊為單元來分配,進程所分配的內存可能比所要的要大。這兩個數字之差稱為內部碎片(internal fragmentation)這部分內存在分區內,但又不能使用。
一種解決外部碎片問題的方法是緊縮(compaction),緊縮的目的是移動內存內容,以便全部空暇空間合并成一整塊。可是緊縮并不是總是可能的。假設重定位是靜態的。而且在匯編時或裝入時進行的,那么就不能緊縮。
緊縮僅在重定位是動態的并在運行時可採用。假設地址被動態重定位,能夠首先移動程序和數據,然后再跟據新基地址的值來改變基地址寄存器。假設採用緊縮。還要評估其開銷。最簡單的合并算法是簡單地將全部進城移到內存的一端。而將全部的孔移到內存的還有一端。以生成一個大的空暇塊。
這樣的方案開銷較大。
還有一種解決方法外部碎片問題的方法是同意物理地址為非連續的。這樣僅僅要有物理內存就能夠為進程分配。這樣的方案有兩種互補的實現技術:分頁和分段。這兩種技術也能夠合并。
轉載于:https://www.cnblogs.com/lxjshuju/p/7281990.html
總結
以上是生活随笔為你收集整理的操作系统概念学习笔记 15 内存管理(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喵喵喵?
- 下一篇: RTP/RTSP/RTCP 协议详解