背景知識
- 背景:內存是現代計算機運行的核心。內存由一個很大的字節數組來組成,每個字節都有各自的地址。CPU根據程序計數器的值從內存中提取指令,這些指令可能引起對特定內存地址的額外加載與存儲
硬件基礎
- 程序必須裝入內存才能被執行
- CPU可以直接訪問的存儲器只有主存高速緩存和寄存器
- 寄存器通常可在1個(或少於1個)CPU時鍾周期內完成訪問,完成主存訪問可能需要多個CPU時鍾周期
- CPU暫停(Stall):在讀取內存數據時,CPU空閑
- Cache位於主存和CPU寄存器之間,協調速度差異
- 內存保護需要保證正確的操作
- 單道程序的內存:
- 多道程序的內存:
內存管理目的和功能
- 目的:
- 提高內存利用率
- 提高指令執行速度
- 保證指令安全運行
- 功能:
- 內存分配
- 內存回收
- 地址轉換
- 存儲保護
- 內存共享
邏輯地址和物理地址
- 邏輯地址:
- 也稱:虛擬地址、程序地址
- 由CPU產生
- 在進程內的相對地址
- 邏輯地址空間:由程序所生成的所有邏輯地址的集合
- 例:
- 物理地址:
- 也稱:絕對地址、實地址
- 內存地址
- 所有內存同一編址
- 物理地址空間:邏輯地址空間對應的所有物理地址的集合
- 例:
獨立運行內存空間
- 需要確保每個進程都有一個單獨的內存空間。單獨的進程內存空間可以保護進程而不互相影響,這對將多個進程加到內存以便並發執行來說至關重要。
- 內存空間保護的實現是通過CPU硬件對在用戶模式下產生的地址與寄存器的地址進行比較來完成的
- 只有操作系統可以通過特殊的特權指令,才能加載基地址寄存器和界限地址寄存器,這種方案允許操作系統修改這兩個寄存器的值,而不允許用戶程序修改他們
- 在內核模式下執行的操作系統可以無限制地訪問操作系統及用戶地內存
- 基址寄存器:進程最小的合法物理內存地址
- 界限寄存器:進程地址的長度
- CPU在執行指令時,需要進行地址合法性驗證
地址綁定
指令和數據綁定到內存
- 程序以二進制可執行文件的形式存儲在磁盤上,為了執行,程序被調入內存並放在程序空間內
- 地址綁定(重定位):在程序裝入內存時,把程序中的相對地址轉換為內存中的絕對地址的過程
- 指令和數據綁定到內存地址可在三個不同階段:
- 編譯時期:
- 如果內存位置已知,可生成絕對代碼(absolute code)
- 如果開始位置改變,需要重新編譯代碼
- 加載時器:
- 如果存儲位置在編譯時不知,則必須生成可重定位代碼( relocatable code),綁定會延遲到加載時進行
- 執行時期:
- 如果進程執行時可在內存移動,則地址綁定可延遲到執行時
- 需要硬件對地址映射的支持(例如基址和界限寄存器)
- 編譯時期:
- 在這些步驟中,地址可能會有不同表示形式
- 源程序中的地址通常是用符號表示(如變量count)
- 編譯器通常將這些符號地址綁定到可重定位的地址
- 鏈接程序或加載程序再將這些可重定位的地址綁定到絕對地址
- 每次綁定都是從一個地址空間到另一個地址空間的映射
內存管理單元(MMU)
- 內存管理單元是把虛擬地址映射到物理地址的硬件
- 是CPU用來管理內存的控制線路
- 在MMU策略中,基址寄存器中的值在其送入內存的時候被加上重定位寄存器的值
- 用戶程序對應到的是邏輯地址,絕不會看到真正的物理地址
- 只有當它作為內存地址時,它才會相對於基地址寄存器進行重定位
- 用戶程序處理邏輯地址
- 內存映射硬件將邏輯地址轉變為物理地址
動態加載
- 例程在調用之前並不加載
- 優點:
- 更好的內存空間利用率
- 沒有被使用的例程不被載入
- 當需大量代碼來處理不經常使用的功能時非常有用
- 不需要操作系統的特別支持,通過程序設計實現
- Windows 的動態鏈接庫
動態鏈接
- 鏈接:將各種代碼和數據片段收集並組合成為一個單一文件的過程
- 動態鏈接:那些組成程序的目標文件等到程序要運行時才進行鏈接
- 和各種庫文件的鏈接被推遲到執行時期
- 需要動態裝載技術支持
- 操作系統需要檢查程序是否在進程的內存空間,所以需要操作系統支持
- 這一技術通常用於系統庫,如語言子程序庫,可以減少磁盤空間和內存空間
- 可用於庫的更新
- 一小段代碼 - 存根,用來定位合適的駐留在內存中的庫程序
- 存根用程序地址來替換自己,並開始執行程序
- 例子:
- 動態鏈接技術的實現依賴於動態裝載和動態綁定
- sum需要動態裝載裝入進來,需要動態綁定技術找到物理地址
- 動態綁定:邏輯地址到物理地址的綁定
- 動態鏈接:符號名到地址的轉換
交換
- 一個進程可以暫時被交換(swap)到內存外的一個備份區 ,隨后可以被換回內存繼續執行
- 備份區———是一個固定的足夠大的可以容納所有用戶內存映 像拷貝的快速磁盤;必須提供對這些內存映像的直接訪問
- 滾入,滾出(Roll out, roll in)————交換由於基於優先級的算法而不同,低優先級的進程被換出,這樣高優先級的進程可以被裝入和執行
- 交換時間的主要部分是轉移時間,總的轉移時間直接同交換的內存的數量成比例
- 交換較為耗時(100MB大約4s)
- 交換技術在現代操作系統中一般很少使用
- 常用策略:當空閑內存不夠時采用交換
連續內存分配
概念
- 連續內存分配:為一個用戶程序分配一個連續的內存空間
- 早期內存分配模式,運用於內存較少系統
- 分類:
- 單一連續分配
- 固定分區分配
- 可變分區分配
- 主存通常被分為兩部分:
- 操作系統(通常駐留在低端,因為中斷矢量保存在低端)
- 操作系統可以位於內存低端,也可位於高端,影響這一決定的主要因素是中斷向量的位置,由於中斷向量通常保存在內存低端,因此操作系統通常也駐留在內存低端
- 用戶進程(保存在內存高端)
- 操作系統(通常駐留在低端,因為中斷矢量保存在低端)
單一連續分配
- 分配方式:單道程序環境下,僅裝有一道用戶程序,即整個內存的用戶空間由該程序獨占
- 內存分配管理十分簡單,內存利用率低
- 用於單用戶、單任務OS
- 未采取存儲器保護措施
- 節省硬件
- 方案可行,又不影響系統安全性
固定分區分配
- 最早、最簡單的可行多道程序的內存管理方式
- 分區:預先把可分配的內存空間分割成若干個連續區域
- 每個分區的大小可以相同也可以不同
- 分區大小固定不變,每個分區裝一個且只能裝一個程序
- 內存分配:,如果有一個空閑分區,則分配給進程;進程運行結束時,其分區被收回,重新分配各其他進程
- 分區大小一樣:
- 缺乏靈活性(程序太小:浪費內存;程序太大:裝不下)
- 有些場合適用,如利用一台計算機同時控制多個相同對象
- 分區大小不等:
- 多個小分區
- 適量中分區
- 少量大分區
可變分區分配
- 是固定分區方案的延伸,主要用於批處理系統
- 分區(孔,Hole):可用的內存塊,不同大小的分區分布在整個內存中
- 根據進程的需要,動態的分配內存空間,即當一個進程到來的時候,它將從一個足夠容納它分區中分配內存,分區中未分配的內存仍然是可用的,可以下次再使用
- 操作系統包含以下信息:
- 已分配的分區
- 空的分區-空閑分區表
存儲分配算法
- 首次適應(First-fit):分配先找到的合適的分區
- 最佳適應(Best-fit):搜索整個序列,找到適合條件的小的分區進行分配
- 最差適應(Worst-fit):搜索整個序列,尋找大的分區進行分配
- 在速度和存儲空間的利用上,首次適應和佳適應要好於差適應。首次適應法和佳適應法在空間上利用差不多,但首次適應法更快些。
回收方法
- a.回收內存塊前后無空閑塊
- b.回收內存塊前有后無空閑塊
- c.回收內存塊前無后有空閑塊
- d.回收內存塊前后均有空閑塊
碎片
- 外碎片:整個可用內存空間可以用來滿足一個請求,但它不是連續的
- 首次適應法和佳適應法都有這個問題,這個問題可能很嚴重,壞的情況下,每兩個進程之間都有空閑塊被浪費
- 屬於系統
- 內碎片:分配的內存可能比申請的內存大一點,外碎片和內碎片兩者之間的差別是內碎片在分區內部,但又不被使用
- 屬於進程
- 可通過緊縮來減少外碎片
- 把一些小的空閑內存結合成一個大的塊
- 只有重定位是動態的時候,並且在運行時,才有可能進行緊縮
- 簡單的合並算法是簡單地將所有進程移動到內存的一端,所有的空閑分區移動到另一端,這種方法開銷較大。為減少開銷,可以選擇移動內容小的一種
- 另一種方案:允許進程的邏輯地址空間是不連續的;這樣,只要物理內存可用,就允許為進程分配內存
分頁
- 分頁內存管理方案————現代操作系統常用方案
- 需要操作系統和計算機硬件的協作
基本方法
- 進程邏輯地址空間可能不連續
- 如果有可用的物理內存,它將分給進程
- 幀:把物理內存分成大小固定的塊
- 大小為2的冪,根據計算機結構不同大小不同
- 早期:512字節至8K字節
- 現在:4K-64K字節
- 頁:把邏輯內存也分位固定大小的塊
- 頁大小(與幀大小一樣)是由硬件來決定的
- 系統保留所有空閑幀的記錄
- 運行一個有N頁大小程序,需要找到N個空幀來裝入程序
- 建立一個頁表,把邏輯地址轉換為物理地址
- 分頁本身是一種動態的重定位
- 每個邏輯地址由分頁硬件綁定為某個物理地址
- 采用分頁類似於采用一組基址(重定位)寄存器,每個基址對應着一個內存幀
- 不會產生外碎片:每個空閑幀都可以分配給需要它的進程
- 存在內碎片
- 分頁內存系統的物理內存的大小不同於進程的最大邏輯大小
- 當進一步探索分頁時,將引入其他的信息,這個消息應保存在頁表條目中
- 該信息也減少了可用於幀地址的位數
- 因此,一個具有32位頁表條目的系統可訪問的物理內存可能小於最大值
- 分頁的一個重要方面是,程序員視圖的內存和實際的物理內存的清楚分離
- 操作系統通過幀表數據結構管理物理內存,在幀表中,每個條目對應着一個幀,以表示該幀是空閑還是已占用;如果占用,是被那個(哪些)進程的那個頁所占用
- 邏輯內存和物理內存的分頁地址
地址轉換機制
- 分頁地址被分為:
- 頁號:包含每個頁在物理內存中的基址,用來作為頁表的索引
- 頁偏移:同基址相結合,用來確定送入內存設備的物理內存地址
- 線性地址和二維地址
頁表的實現
- 頁表被保存在主存中
- 頁表基址寄存器(PTBR)指向頁表
- 頁表限長寄存器(PRLR)表明頁表的長度
- 在這個機制中,每一次的數據/指令存取需要兩次內存存取,一次是存取頁表,一次是存取數據/指令
- 解決兩次存取的問題,是采用小但專用且快速的硬件緩沖,這種緩沖稱為轉換表緩沖器(TLB)或聯想寄存器
- TLB是一個硬件功能,因此操作系統及其設計師似乎不必關心。但是設計師需要了解TLB的功能和特性,它們因硬件平台的不同而不同
- 硬件功能對內存性能有着顯著的影響,而操作系統的改進(如分頁)能導致硬件的改進並反過來受其影響
- 聯想寄存器:並行查找
- 地址轉換 (A´, A´´) ;如果A´在聯想寄存器中,把幀號取出來 ;否則從內存中的頁表中取出幀號
- 有效訪問時間(EAT):
- 聯想寄存器的查找需要時間單位a微秒
- 假設內存一次存取需要b微秒
- 命中率———在聯想寄存器中找到頁號的百分比,比率與聯想寄存器的大小有關
- 命中率 = λ
- EAT = λ (a + b) + (1 – λ) (a+2b)
內存保護
- 簡單方法:
- 把頁號和頁表限長寄存器(PRLR)比較
- 內存的保護由與每個幀相連的保護位來實現
- 有效-無效位附在頁表的每個表項中:
- 有效表示相關的頁在進程的邏輯地址空間,並且是一個合法的頁
- 無效表示頁不在進程的邏輯地址空間中
頁共享
- 共享代碼:
- 如果代碼是可重入代碼(只讀),可以在進程間共享(如文本編輯器, 編譯器, 數據庫系統)
- 共享代碼必須出現在所有進程的邏輯地址空間的相同位置
- 私有代碼和數據:
- 每個進程保留一個代碼和數據副本
- 存有私有數據和代碼的頁能夠出現在邏輯地址空間的任意位置
- 例:
頁表結構
- 例:
- 32位邏輯地址
- 頁大小為4KB(也就是212)
- 那么一個頁表多可包含1M(232/212=220)個表項
- 假設每個頁表項占用4個字節,那么每個進程需要4MB物理空間,也就是1024個連續頁面來存儲頁表
- 需要這么多個連續頁面來存放頁表不一定能實現
- 解決方法:
- 層次頁表
- 哈希頁表
- 反向頁表
層次頁表
- 兩級分頁:
- 一個邏輯地址被分為(在4K頁大小的32位系統上):
- 一個20位的頁號
- 一個12位的頁偏移
- 由於頁表所在頁也被分頁,頁號被進一步分為:
- 一個10位的頁號
- 一個10位的頁偏移
- 因此,邏輯地址表示如下:
- 地址轉換機制:
- 由於地址轉換由外向內,這種方案也稱為向前映射頁表
- 一個邏輯地址被分為(在4K頁大小的32位系統上):
- 可依據實際情況進行多次分頁
- 若頁表划分為N級,則需要訪問內存N+1次
- 如系統中存在快表,則在快表命中時,只需要訪問一次內存即可
哈希頁表
- 通常地址空間大於32位
- 采用虛擬頁碼作為哈希值
- 哈希頁表的每一個條目都包括一個鏈表,該鏈表的元素哈希到同一位置(該鏈表用來解決處理碰撞)
- 每個元素由三個字段組成:虛擬頁碼、映射的幀碼、指向鏈表內下一個元素的指針
- 虛擬頁號與鏈表中的每個元素相比較,找到匹配項。如果匹配,則相應的物理幀被取出
反向頁表
- 對於每個正真的內存頁或幀才有一個條目
- 每個條目保存在正真內存位置的頁的虛擬地址,以及包括擁有這個頁的進程的信息
- 反向頁表的條目中需要一個地址空間標識符,以確保一個特定進程的一個邏輯頁可以映射到相應的物理幀
- 討論:
- 減少了需要儲存每個頁表的內存,但是當訪問一個頁時,增加了尋找頁表需要的時間
- 使用哈希表來將查找限制在一個或少數幾個頁表條目
- 實現共享內存困難,每個物理頁只有一個虛擬頁條目,所以一個物理頁不可能有兩個或更多的共享虛擬地址
- 解決方案:只允許頁表包含一個虛擬地址到共享物理地址的映射,這意味着,對未映射的虛擬地址的引用會導致頁錯誤
- 解決方案:只允許頁表包含一個虛擬地址到共享物理地址的映射,這意味着,對未映射的虛擬地址的引用會導致頁錯誤
分段
- 分頁內存管理存在的問題
- 用戶視角的內存和實際物理內存的分離,即用戶視角的內存與實際物理地址不一樣
- 沒有用戶願意將內存看作一個線性字節數值,程序員通常願意將內存看成一組長度不同的段的集合,段是邏輯上有意義的單位,而且段與段之間沒有一定的順序
- 存在外碎片的可能性小,段小可用分配到小內存
基本方法
- 支持用戶觀點的內存管理機制
- 一個程序是一些段的集合,一個段是一個邏輯單位
- 邏輯地址空間是由一組段組成的,每個段都有名稱和長度,地址指明了段名稱和段內偏移
- 分段邏輯地址是二維,物理地址是一維
- 一個邏輯地址是兩個向量的集合
- <segment-number, offset>
- 通常,在編譯用戶程序時,編譯器會根據輸入程序來自動構造段
- 在編譯時鏈接的庫可能分配不同的段。加載程序會裝入所有這些段,並為它們分配段號
- 分段的邏輯視圖:
分段機制-1
- 一個邏輯地址是兩個向量的集合
- <segment-number, offset>
- 段表(segment table)——映射二維物理地址,每個表項包括:
- 基址base —— 包括內存中段物理地址的起始地址
- 限長limit —— 指定段的長度
- 段表基址寄存器(STBR)指向段表在內存中的地址
- 段表限長寄存器(STLR)表明被一個程序所使用的段的數目
- 如果s<STLR,段號s是合法的
- 分段硬件:
- 分段例子:
分段機制-2
- 由於段的長度各不相同,因此這種內存分配實際上是一個動態存儲分配問題,可采用可變分區方案實現
- 內存分配:
- 首先/佳適應法
- 外碎片問題
- 重定位
- 動態
- 由段表來執行
- 共享
- 共享的段
- 同樣的段號
- 段是邏輯上有意義的單位
- 實現更方便
- 比頁共享更容易實現
- 分段共享例子:
分段機制-3
- 保護,每個段表的表項有
- 有效位 = 0 ⇒ 非法段
- 讀/寫/執行權利
- 保護位同段相聯系,在段的級別進行代碼共享
段頁式
原理
- 分段和分頁原理的結合
- 基本原理:先將用戶程序分成若干段,並為每個段賦予一個段號,再把每個段號分成若干個頁
- 邏輯地址:<段號,頁號,頁內偏移>
- 從用戶視角看,程序被划分成了邏輯上有意義的段
- 從系統角度看,物理內存被划分成了固定大小的幀
- 存在內碎片
- 無外碎片
例子
- Intel386的段頁式存儲管理:
- Intel 32和64-bit CPU段頁式結構
- 32位:IA-32 (64位:IA-64)
- 支持分段和段頁式
- 每段最大:4GB
- 每個進程最多:16K段
- 分成2部分
- 第一部分:大8K個段是進程私有(保存在local descriptor table (LDT))
- 第二部分:第二部分:大8K個段私有進程共享(保存在global descriptor table (GDT))
- IA-32地址轉換:
- Intel IA-32分段結構:
- Intel IA-32分頁結構:
內存“擴充”技術
- 內存空間不足怎么辦?-解決方法:
- 緊縮Compaction(可變分區)
- 覆蓋技術Overlaying
- 交換技術Swapping
- 虛擬內存Virtual Memory
覆蓋
- 待解決問題:程序大小超過物理內存總和
- 程序執行時:
- 只在內存中保留哪些在任何時間都需要的指令和數據
- 程序的不同部分在內存中相互替換
- 其他部分根據邏輯將不會同時執行的程序段,共享同一塊內存區域
- 優點:由程序員聲明覆蓋結構,不需要操作系統的特別支持
- 缺點:覆蓋結構的程序設計很復雜
- 應用於早期的操作系統
- 例:
交換
- 在多道程序環境下:
- 一方面,在內存中的某些進程由於某事件尚未發生而被阻塞運行,但它卻占用了大量的內存空間,甚至有時可能出現在內存中所有進程都被阻塞而迫使CPU停止下來等待的情況
- 另一方面,卻又有着許多作業在外存上等待,因無內存而不能進入內存運行的情況
- 浪費資源,降低系統吞吐量
- 一個進程可以暫時被交換(swap)到內存外的一個備份區,以便騰出足夠的內存空間,再把已經具備運行條件的進程或進所需的程序或數據,調入內存,隨后可以被換回內存繼續執行
- 備份區:是一個固定的足夠大的可以容納所有用戶內存映像拷貝的快速磁盤;必須提供對這些內存映像的直接訪問
- 示意圖:
- 特點:
- 滾入,滾出(Roll out, roll in):交換由於基於優先級的算法而不同,低優先級的進程被換出,這樣高優先級的進程可以被裝入和執行
- 交換時間的主要部分是轉移時間,總的轉移時間直接同交換的內存的數量成正比
- 交換較為耗時(100MB大約4s)
- 交換時間的主要部分是轉移時間,總的轉移時間直接同交換的內存數量成正比
- 標准交換技術在現代操作系統中一般很少使用
- 常用策略:當空閑內存不夠時采用交換(如Unix)
- 常用策略:當空閑內存不夠時采用交換(如Unix)
需要考慮的問題
- 進程的哪些內存要交換到磁盤?
- 運行時創建或修改的內容:棧和堆
- 在磁盤的什么位置保存被換出的進程?
- 交換區(備份區):系統指定一塊特殊的磁盤區域作為交換空間(swap space),包含連續的磁道,操作系統可以使用底層的磁盤讀寫操作對其高效訪問
- 不同操作系統交換區的叫法不同
- Windows系統成為頁文件pagefile
- 何時需要發生交換?
- 一種是進程只要不用就換出(很少再用)
- 第二種使用更多的策略是內存空間不夠或有不夠的危險時啟動交換程序換出(Linux就是采用這種策略)
- 這個過程與調度器結合起來
- 如何選擇被換出的進程?
- 考慮進程的各種屬性:哪些進程不應該換出
- 如緊縮技術,有些進程在內存里是不能隨意搬家的
- 處於等待I/O狀態的進程也不能被隨意交換出去
- 如何處理進程空間增長?
- 一般采用預留空間的方法
- 一般采用預留空間的方法