物理內存管理:連續內存分配
地址空間定義
- 物理地址空間:硬件支持的地址空間
- 起始地址0,直到 MAXsys
- 邏輯地址空間:在 CPU 運行的進程看到的地址
- 起始地址0,直到 MAXprog
地址生成時機和限制
- 編譯時
- 假設起始地址已知
- 如果起始地址改變,必須重新編譯
- 加載時
- 如編譯時起始位置未知,編譯器需生成可重定位的代碼(relocatable code)
- 加載時,生成絕對地址
- 執行時
- 執行時代碼可移動
- 需地址轉換(映射)硬件支持
地址生成過程
- CPU
- ALU:需要邏輯地址的內存內容
- MMU:進行邏輯地址和物理地址的轉換
- CPU 控制邏輯:給總線發送物理地址請求
- 內存
- 發送物理地址的內容給 CPU
- 或接受 CPU 數據到物理地址
- 操作系統
- 建立邏輯地址 LA 和物理地址 PA 的映射
連續內存分配和內存碎片
- 連續內存分配
- 給進程分配一塊不小於指定大小的連續的物理內存區域
- 內存碎片
- 空閒內存不能被利用
- 外部碎片
- 分配單元之間的未被使用內存
- 內部碎片
- 分配單元內部的未被使用內存
- 取決於分配單元大小是否要取整
連續內存分配:動態分區分配
- 動態分區分配
- 當程序被加載執行時,分配一個進程指定大小可變的分區(塊、內存塊)
- 分區的地址是連續的
- 操作系統需要維護的數據結構
- 所有進程的已分配分區
- 空閒分區(Empty-blocks)
- 動態分區分配策略
- 最先匹配(First-fit)
- 最佳匹配(Best-fit)
- 最差匹配(Worst-fit)
最先匹配(First Fit Allocation)策略
- 思路:分配 n 個字節,使用功能第一個可用的空間比 n 大的空閒塊
- 原理 & 實現
- 空閒分區列表按地址順序排序
- 分配過程中,搜索一個合適的分區
- 釋放分區時,檢查是都可與臨近的空閒分區合併
- 優點
- 簡單
- 在高地址空間有大塊的空閒分區
- 缺點
- 外部碎片
- 分配大塊時較慢
最佳匹配(Best Fit Allocation)策略
- 思路:分配 n 字節分區時,查找並使用不小於 n 的最小空閒分區
- 原理 & 實現
- 空閒分區列表按照大小排序
- 分配時,查找一個合適的分區
- 釋放時,查找並且合併臨近的空閒分區(如果找到)
- 優點
- 大部分分配的尺寸較小時,效果很好
- 可避免大的空閒分區被拆分
- 可減少外部碎片的大小
- 相對簡單
- 大部分分配的尺寸較小時,效果很好
- 缺點
- 外部碎片
- 釋放分區比較慢
- 容易產生很多無用的小碎片
最差匹配(Worst Fit Allocation)策略
- 思路:分配 n 字節,使用尺寸不小於 n 的最大空閒分區
- 原理 & 實現
- 空閒分區列表按由大到小排序
- 分配時,選最大的分區
- 釋放時,檢查是否可與臨近的空閒分區合併,進行可能的合併,並調整空閒分區列表順序
- 優點
- 中等大小的分配比較多時,效果最好
- 避免出現太多的小碎片
- 缺點
- 釋放分區較慢
- 外部碎片
- 容易破壞大的空閒分區,因此後續難以分配大的分區
碎片整理:緊湊( compaction )
- 碎片整理
- 通過調整進程佔用的分區位置來減少或避免分區碎片
- 碎片緊湊
- 通過移動分配給進程的內存分區,以合併外部碎片
- 碎片緊湊的條件:所有的應用程序可動態重定位
- 需要解決的問題:
- 什麼時候移動?
- 開銷
碎片整理:分區兌換( Swapping in/out )
- 通過搶佔並回收處於等待狀態進程的分區,以增大可用內存空間
- 需要解決的問題
- 交換哪個(些)程序?
夥伴系統( Buddy System )
- 整個可分配的分區大小 2U
- 需要的分區大小爲 2U-1 < s <= 2U 時,把整個塊分配給該進程
- 如 s <= 2i-1 ,將大小爲 2i 的當前空閒分區劃分成兩個大小爲 2i-1 的空閒分區
- 重複劃分過程,直到 2i-1 < s <= 2i ,並把一個空閒分區分配給該進程
夥伴系統的實現
- 數據結構
- 空閒塊按大小和起始地址組織成二維數組
- 初始狀態:只有一個大小爲 2U 的空閒塊
- 分配過程
- 由小到大在空閒塊數組中找最小的可用空閒塊
- 如空閒塊過大,對可用空閒塊進行二等分,知道得到合適的可用空閒塊
- 釋放過程
- 把釋放的塊放入空閒塊數組
- 合併滿足合併條件的空閒塊
- 合併條件
- 大小相同 2i
- 地址相鄰
- 起始地址較小的塊的起始地址必須是 2^(i+1) 的倍數
物理內存管理:非連續內存分配
非連續分配的設計目標
- 連續分配的缺點
- 分配給程序的物理內存必須連續
- 存在外碎片和內碎片
- 內存分配的動態修改困難
- 內存利用率較低
- 非連續分配的設計目標:提高內存利用效率和管理靈活性
- 允許一個程序的使用非連續的物理地址空間
- 允許共享代碼與數據
- 支持動態加載和動態鏈接
- 非連續分配需要解決的問題
- 如何實現虛擬地址和物理地址的轉換?
- 軟件實現 (靈活,開銷大)
- 硬件實現 (夠用,開銷小)
- 如何實現虛擬地址和物理地址的轉換?
- 非連續分配的硬件輔助機制
- 如何選擇非連續分配中的內存分塊大小?
- 段式存儲管理( segmentation )
- 頁式存儲管理( paging )
- 如何選擇非連續分配中的內存分塊大小?
段地址空間
- 進程的段地址空間由多個段組成
- 主代碼段
- 子模塊代碼段
- 公用庫代碼段
- 堆棧段( stack )
- 堆數據( heap )
- 初始化數據段
- 符號表等
- 段式存儲管理的目的
- 更細粒度和靈活的分離與共享
段訪問機制
- 段的概念
- 段表示訪問方式和存儲數據等屬性相同的一段地址空間
- 對應一個連續的內存"塊"
- 若干個段組成進程邏輯地址空間
- 段訪問:邏輯地址由二元組 (s,addr) 表示
- s: 段號
- addr: 段內偏移
頁式存儲管理
- 頁幀(幀,物理頁面,Frame,Page Frame)
- 頁面(頁,邏輯頁面,Page)
- 頁面到頁幀
- 邏輯地址到物理地址的轉換
- 頁表
- MMU/TLB
幀( Frame )
- 物理內存被劃分成大小相等的幀,內存物理地址的表示:二元組 (f,o)
f: 幀號( F 位,共有 2F 個幀)
o: 幀內偏移 ( S 位,每幀有 2S 字節)
物理地址 = f * 2S + o - 基於頁幀的物理地址計算實例
- 假定
- 16-bit 的地址空間
- 9-bit (512 byte) 大小的頁幀
- 物理地址計算
- 物理地址表示 = (3, 6)
- 物理地址 = f * 2S + o
F = 7,S = 9,f = 3,o = 6
- 實際物理地址 = 2^9 * 3 + 6 = 1536 + 6 = 1542
- 假定
頁( Page )
- 進程邏輯地址空間被劃分爲大小相等的頁
- 頁內偏移 = 幀內偏移
- 通常:頁號大小 ≠ 幀號大小
- 進程邏輯地址的表示:二元組 (p, o)
p: 頁號 ( P 位,2P 個頁)
o: 頁內偏移 ( S 位,每頁有 2S 字節)
虛擬地址 = p * 2S + o
- 頁式存儲中的地址映射
- 頁到幀的映射
- 邏輯地址中的頁號是連續的
- 物理地址中的幀號是不連續的
- 不是所有的頁都有對應的幀
頁表
- 頁表概述
- 頁表保存了邏輯地址與物理地址之間的映射關係
- 頁表結構
- 每個進程都有一個頁表
- 每個頁面對應一個頁表項
- 隨進程運行狀態而動態變化
- 頁表基址寄存器( PTBR: Page Table Base Register )
- 頁表項的組成
- 幀號:f
- 頁表項狀態
- 存在位( register bit )
- 修改位( dirty bit )
- 引用位( clock/reference bit )
- 每個進程都有一個頁表
- 頁式存儲管理機制的性能問題
- 內存訪問性能問題
- 訪問一個內存單元需要2次內存訪問
- 第一次訪問:獲取頁表項
- 第二次訪問:訪問數據
- 頁表大小問題
- 頁表可能非常大
- 64位機器如果每頁1024字節,那麼一個頁表的大小會是多少
- 如何處理?
- 緩存 ( Caching )
- 間接 ( Indirection ) 訪問
- 內存訪問性能問題
解決頁表問題
- 快表 ( Translation Look-aside Buffer, TLB )
- 緩存近期訪問的頁表項
- TLB 使用關聯存儲 ( association memory ) 實現,具備快速訪問性能
- 如果 TLB 命中,物理頁號可以很快被獲取
- 如果 TLB 未命中,對應的表項被更新到 TLB 中
- 緩存近期訪問的頁表項
- 多級頁表
- 通過間接引用將頁號分成 k 級
- 建立頁表"樹"
- 減少每級頁表的長度
- 通過間接引用將頁號分成 k 級
- 反置頁表
- 大地址空間問題
- 對於大地址空間 ( 64-bits ) 系統,多級頁表變得繁瑣
- 比如:5級頁表
- 邏輯(虛擬)地址空間增長速度快於物理地址空間
- 頁寄存器和反置頁面的思路
- 不讓頁表與邏輯地址空間的大小相對應
- 讓頁表與物理地址空間的大小相對應
- 對於大地址空間 ( 64-bits ) 系統,多級頁表變得繁瑣
- 頁寄存器( Page Registers )
- 每個幀與一個頁寄存器( Page Register )關聯,寄存器內容包括:
- 使用位( Register bit ):此幀是否被進程佔用
- 佔用頁號( Occupier ):對應的頁號 p
- 保護位 ( Protection bits )
- 頁寄存器示例
- 物理內存大小:4096 * 4096 = 4 K * 4 KB = 16 MB
- 頁面大小:4096 bytes = 4 KB
- 頁幀數:4096 = 4 K
- 頁寄存器使用的空間(假設每個頁寄存器佔8字節):8 * 4096 = 32 Kbytes
- 頁寄存器帶來的額外開銷:32K / 16M = 0.2%(大約)
- 虛擬內存的大小:任意
- 頁寄存器方案特徵
- 優點
- 頁表大小相對於物理內存而言很小
- 頁表大小與邏輯地址空間大小無關
- 缺點
- 頁表信息對調後,需要依據幀號可找頁號
- 在頁寄存器中搜索邏輯地址中的頁號
- 優點
- 頁寄存器中的地址轉換
- CPU 生成的邏輯地址如何找對應的物理地址?
- 對邏輯地址進行 Hash 映射,以減少搜索範圍
- 需要解決可能的衝突
- 用快表緩存頁表項後的頁寄存器搜索步驟
- 對邏輯地址進行 Hash 變換
- 在快表中查找對應頁表項
- 有衝突時遍歷衝突項鍊表
- 查找失敗時,產生異常
- 快表的限制
- 快表的容量限制
- 快表的功耗限制( StrongARM 上快表功耗佔 27% )
- CPU 生成的邏輯地址如何找對應的物理地址?
- 每個幀與一個頁寄存器( Page Register )關聯,寄存器內容包括:
- 反置頁表
- 基於 Hash 映射值查找對應頁表項中的幀號
- 進程標識與頁號的 Hash 值可能有衝突
- 頁表項中包括保護位、修改位、訪問位和存在位等標識
- 反置頁表的 Hash 衝突
- 基於 Hash 映射值查找對應頁表項中的幀號
- 大地址空間問題
段頁式存儲管理
- 需求
- 段式存儲在內存保護方面有優勢,頁式存儲在內存利用和優化轉移到後備存儲方面有優勢。
- 段式存儲,頁式存儲能否結合?
- 做法
- 在段式存儲管理基礎上,給每個段加一級頁表
- 段頁式存儲管理中的內存共享
- 通過指向相同的頁表基址,實現進程間的段共享