操作系統-第九章-虛擬內存管理


背景

  • 代碼必須裝入內存才能執行,但是並不是所有代碼必須全部裝入內存
    • 錯誤代碼
    • 不常用的函數
    • 大的數據結構
  • 局部性原理:一個程序只要部分裝入內存就可以運行
    • 整個程序不是同一時間都要運行
  • 程序部分裝入技術優點
    • 進程大小不再受到物理內存大小限制,用戶可以在一個虛擬的地址空間編程,簡化了編程工作量
    • 每個進程需要的內存更小,因此更多進程可以並發運行,提供了CPU的利用率
    • I/O更少(載入的內容更少),用戶程序運行更快

局部性原理

  • 即在一較短的時間內,程序的執行僅局限於某個部分;相應地,它所訪問的存儲空間也局限於某個區域
    • 程序執行時,除了少部分的轉移和過程調用外,在大多數情況下仍然是順序執行的
    • 過程調用將會使程序的執行軌跡由一部分區域轉至另一部分區域,過程調用的深度一般小於5。程序將會在一段時間內都局限在這些過程的范圍內運行
    • 程序中存在許多循環結構,多次執行
    • 對數據結構的處理局限於很小的范圍

虛擬內存

  • 虛擬存儲技術:當進程運行時,先將其一部分裝入內存,另一部分暫留在磁盤,當要執行的指令或訪問的數據不在內存時,由操作系統自動完成將它們從磁盤調入內存執行
  • 虛擬地址空間:分配給進程的虛擬內存
  • 虛擬地址:在虛擬內存中指令或數據的位置
  • 虛擬內存:把內存和磁盤有機結合起來使用,得到一個容量很大的“內存”,即虛存
    • 區分開物理內存和用戶邏輯內存
    • 只有部分運行的程序需要在內存中
    • 邏輯地址空間能夠比物理地址空間大
    • 必須允許頁面能夠被換入和換出
    • 允許更有效的進程創建
  • 虛存是對內存的抽象,構建在存儲體系之上,由操作系統來協調各存儲器的使用
  • 虛擬內存大於物理內存
  • 虛擬存儲器的大小由2個因素決定:
    • 操作系統字長
    • 內存外存容量
  • 使用虛擬內存的共享庫:
    • 通過將共享對象映射到虛擬地址空間,系統庫可用被多個進程共享
    • 虛擬內存允許進程共享內存
    • 虛擬內存可允許在創建進程期間共享頁,從而加快進程創建

寫時復制

  • 寫時復制允許父進程和子進程在初始化時共享頁面
    • 如果其中一個進程修改了一個共享頁面,會產生副本
    • 更加高效
    • 應用在Windows XP,Linux等系統
  • 當確定采用寫時復制頁面時,重要的是注意空閑頁面的分配位置
    • 許多操作系統為這類請求提供了一個空閑的頁面池
    • 當進程的堆棧或堆要擴展時或有寫時復制頁面需要管理時,通常分配這些空閑頁面。
    • 操作系統分配這些頁面通常采用按需填零的技術。按需填零頁面在需要分配之前先填零,因此清除了以前的內容
  • vfork:fork()變形,不使用寫時復制
    • 父進程被掛起,子進程使用父進程的地址空間
    • 如果子進程修改父地址空間的任何頁面,那么這些修改過的頁面對於恢復的父進程是可見的
    • 應謹慎使用vfork(),以確保子進程不會修改父進程的地址空間
    • 當子進程在創建后立即調用exec()時,可使用vfork()
    • 因為沒有復制頁面,vfork()是一個非常有效的進程創建方法
  • 例子:


虛擬內存的實現

  • 虛擬內存能夠通過以下手段來執行實現:
    • 虛擬頁式(虛擬存儲技術+頁式存儲管理)
    • 虛擬段式(虛擬存儲技術+段式存儲管理)
  • 虛擬頁式有兩種方式:
    • 請求分頁( Demand paging )
    • 預調頁(Prepaging)
      • 以預測為基礎,將預計不久后便會被訪問的若干頁面,預先調入內存

請求分頁

基本思想(虛擬頁式存儲管理)

  • 進程開始運行之前,不是裝入全部頁面,而是裝入一個或零個頁面
    • 裝入零個頁面的調頁為:純請求調頁
  • 運行之后,根據進程運行需要,動態裝入其他頁面
  • 當內存空間已滿,而又需要裝入新的頁面時,則根據某種算法置換內存中的某個頁面,以便裝入新的頁面

按需調頁

  • 只有在一個頁需要的時候才把它換入內存
    • 需要很少的I/O
    • 需要很少的內存
    • 快速響應
    • 支持多用戶
  • 類似交換技術,粒度不同
    • 交換程序(swapper)對整個進程進行操作
    • 調頁程序(pager)只是對進程的單個頁進行操作
  • 需要頁⇒ 查閱此頁
    • 無效的訪問 ⇒ 中止
    • 不在內存 ⇒ 換入內存
  • 懶惰交換:只有在需要頁時,才將它調入內存
    • 交換程序(swapper)對整個進程進行操作
    • 調頁程序(pager)只是對進程的單個頁進行操作
  • 調頁程序不是調入整個進程,而是把哪些要使用的頁調入內存
    • 調頁程序就避免了讀入哪些不使用的頁,也減少了交換時間和所需的物理內存空間
    • 有效-無效位方案實現

有效-無效位

  • 在每一個頁表的表項有一個有效-無效位相關聯,1表示在內存,0表示不在內存
  • 在所有的表項,這個位被初始化為0
  • 在地址轉換中,如果頁表表項位的值是0 ⇒缺頁中斷(page fault)

缺頁中斷(頁錯誤)

  • 如果對一個頁的訪問,首次訪問該頁需要陷入OS ⇒ 缺頁中斷
  • 1.訪問指令或數據
    • 發現有效無效位為0
  • 2.查看另一個表來決定
    • 無效引用 ⇒ 終止
    • 僅僅不在內存
  • 3.找到頁在后備存儲上的位置
  • 4.得到空閑幀,把頁換入幀
  • 5.重新設置頁表,把有效位設為v
  • 6.重啟指令: 近未使用


請求分頁討論

  • 極端情況:進程執行第一行代碼時,內存內沒有任何代碼和數據
    • 進程創建時,沒有為進程分配內存,僅建立PCB
    • 導致缺頁中斷
    • 純請求分頁
  • 一條指令可能導致多次缺頁(涉及多個頁面)
    • 幸運的是,程序具有局部性(locality of reference)
  • 請求分頁需要硬件支持
    • 帶有效無效位的頁表
    • 交換空間
    • 指令重啟(請求調頁的關鍵要求是在缺頁錯誤后重新啟動任何指令的能力)

請求分頁的性能

  • 缺頁率(缺頁的概率):0 <= p <= 1.0
    • 如果 p = 0,沒有缺頁
    • 如果 p = 1,每次訪問都缺頁
  • 有效訪問時間(EAT):
    • EAT = (1 – p) x 內存訪問時間+ p x 頁錯誤時間
  • 對於請求調頁,降低缺頁錯誤率是極為重要的。否則,會增加有效訪問時間,從而極大地減緩了進程的執行速度
  • 頁錯誤時間(包含多項處理的時間,主要有三項):
    • 處理缺頁中斷時間
    • 讀入頁時間
    • 重啟進程開銷
    • [頁交換出去時間](不是每次都需要)
    • (注:不包含內存訪問時間:內存訪問時間與頁錯誤時間相比可忽略不計內存訪問時間被包含在其中)

請求分頁性能優化

  • 頁面轉換時采用交換空間,而不是文件系統
    • 交換區的塊大,比文件系統服務快速
  • 在進程裝載時,把整個進程拷貝到交換區
    • 基於交換區調頁
    • 早期的BSD Unix
  • 對於二進制文件的請求調頁,利用文件系統進行交換
    • Solaris和當前的BSD Unix
    • 對於與文件無關的頁面部分內容仍舊需要交換區(堆棧等)

頁面置換

無空閑頁的辦法

  • 解決方法
    • 終止進程
    • 交換進程
    • 頁面置換,又稱頁置換、頁淘汰
  • 頁面置換
    • 找到內存中並沒有使用的一些頁,換出
    • 算法
    • 性能——找出一個導致小缺頁數的算法
    • 同一個頁可能會被裝入內存多次
  • 頁面置換討論
    • 如果發生頁置換,則缺頁處理時間加倍
    • 通過修改缺頁服務例程,來包含頁面置換,防止分配過多
    • 修改(臟)位modify (dirty) bit來防止頁面轉移過多——只有被修改的頁面才寫入磁盤
    • 頁置換完善了邏輯內存和物理內存的划分——在一個較小的物理內存基礎之上可以提供一個大的虛擬內存
  • 需要頁面置換的情況


基本頁面置換

  • 為了實現請求調頁,必須開發兩個算法:
    • 如果在內存中有多個進程,那么\(\color{red}{幀分配算法}\)決定為每個進程各分配多少幀
    • 當發生頁置換時,\(\color{red}{頁置換算法}\)決定要置換的幀是哪一個
  • 基本頁面置換方法
    • 1.查找所需頁在磁盤上的位置
    • 2.查找一空閑頁框
      • 如果有空閑頁框,就使用它
      • 如果沒有空閑頁框,使用頁置換算法選擇一個“ 犧牲”頁框(victim frame)
      • 將“犧牲”幀的內容寫到磁盤上,更新頁表和幀表
    • 3.將所需頁讀入(新)空閑頁框,更新頁表和幀表
    • 4.重啟用戶進程
  • 頁面置換


頁面置換算法

  • 目的
    • 最小的缺頁率
    • 通過運行一個內存訪問的特殊序列(訪問序列),計算這個序列的缺頁次數
  • 要求
    • 掌握設計思想、算法應用
    • 了解部分算法的實現
  • 優置換置換算法(OPT)
  • 先進先出置換算法(FIFO)
  • 最少最近使用置換算法(LRU)
  • 近似LRU算法
    • 二次機會法

先進先出算法(FIFO)

  • 置換在內存中駐留時間長的頁面
  • 容易理解和實現、但性能不總是很好
  • 實現:使用FIFO隊列管理內存中的所有頁
  • FIFO算法可能會產生Belady異常
    • 更多的頁框——更多的缺頁


最優置換算法(OPT)

  • 被置換的頁是將來不再需要的或最遠的將來才會被使用的頁
  • 作用:作為一種標准衡量其他算法的性能


最少最近使用置換算法(LRU)

  • 置換長時間沒有使用的頁
  • 性能接近最優置換算法(OPT)
  • 實現
    • 每一個頁表項有一個計數器(時間戳)或棧
    • 開銷大,需要硬件支持


LRU近似算法

  • 在沒有硬件支持的系統中,可使用LRU近似算法
  • 訪問位(引用位):
    • 每個頁都與一個位相關聯r位,初始值位0
    • 當頁訪問時設位1
  • 基於引用位的算法
    • 附加引用位算法
    • 二次機會算法
    • 增強型二次機會算法
  • 附加引用位算法
    • 為內存中的每個頁設置一個8位字節
    • 在規定時間間隔內,把每個頁的引用位轉移到8位字節的高位,將其他位向右移一位,並舍棄低位
    • 這8位移位寄存器包含近8個時間周期內的頁面使用情況
    • 小值的頁為近少使用頁,可以被淘汰
  • 二次機會算法
    • 需要引用位(訪問位)
    • 如果引用位為0,直接置換
    • 如果將要(以順時針)交換的頁訪問位是1,則:
      • 把引用位(訪問位)設為0
      • 把頁留在內存中
      • 以同樣的規則,替換下一個頁
    • 實現:時鍾置換(順時針方向,采用循環隊列)
    • FIFO的增強算法


基於計數的頁面的置換

  • 不經常使用(NFU)算法
    • 需要一個初值為0的軟件計數器與每頁相聯
    • 每次時鍾中斷時,操作系統掃描內存中的所有頁面,將每頁的R位(其值為0或1)加到其計數器上
    • 缺頁時淘汰計數器值最小的頁
    • 實質:跟蹤每頁被訪問的頻繁程度
  • 老化算法
    • NFU算法修改
    • 先將計數器右移一位
    • 把R位加到計數器的最左端


頁框分配(幀分配)

基本概念

  • 必須滿足:每個進程所需要最少的頁數
    • 隨着分配給每個進程的幀數量的減少,缺頁錯誤率增加,從而減慢進程執行。此外,若在執行指令完成之前發生缺頁錯誤,應重新啟動指令。因此,必須有足夠的幀來容納任何單個指令可以引用的所有不同的頁面
    • 最小幀數由計算機架構定義
    • 盡管每個進程的最小幀數是由體系結構決定的,但是最大幀數是由可用物理內存的數量決定的
  • 兩個主要的分配策略:
    • 固定分配
    • 優先級分配

固定分配

  • 為每個進程分配固定數量的頁框
  • 兩種分配方式:
    • 平均分配(均分法)
      • 在n個進程中分配m個幀的最容易的方法,給每個進程一個平均值,即m/n幀(忽略操作系統所需的幀)
    • 按比率分配(根據每個進程的大小來分配)
      • 基於各個進程需要不同數量的內存
  • 對於平均分配和比例分配,每個進程分得的數量可以因多道程序而變化。如果多道程序增加,則每個進程會失去一些幀,以提供新進程所需的內存。相反,如果多道程度較低,則原來分配給離開進程的幀會分配給剩余進程

優先級分配

  • 根據優先級而不是進程大小來使用比率分配策略
  • 如果進程Pi產生一個缺頁
    • 選擇替換其中的一個頁框
    • 從一個較低優先級的進程中選擇一個頁面來替換

全局置換和局部置換

  • 全局置換
    • 進程在所有的頁框中選擇一個替換頁面;一個進程可以從另一個進程中獲得頁框
  • 局部置換
    • 每個進程只從屬於它自己的頁框中選擇
  • 采用局部置換,分配給每個進程的頁框數量不變;采用全局置換,可能增加所分配頁框的數量,因為可能從分配給其他進程的頁框中選擇一個置換
  • 全局置換的問題,進程不能控制其缺頁率,局部置換沒有這個問題。但局部置換不能使用其他進程不常用的內存
  • 全局置換有更好的系統吞吐量,更為常用

系統抖動

基本概念

  • 如果一個進程沒有足夠的頁,那么缺頁率將很高,這將導致:
    • CPU利用率地下
    • 操作系統認為需要增加多道程序設計的道數
    • 系統中將加入一個新的進程
  • 抖動(顛簸):一個進程的頁面經常換入換出,進程的調頁時間多於它的執行時間
    • 原因:系統內存不足,頁面置換算法不合理

局部置換算法

  • 通過局部置換算法可以限制系統抖動(顛簸)
  • 如果一個進程開始顛簸,那么它不能置換其他進程的頁框
  • 局部模型
    • 進程從一個局部移到另一個局部
    • 局部可能重疊
  • 局部是由程序結構和數據結構來定義的。局部模型指出,所有程序都具有這種基本的內存引用結構
    • 局部性模型是緩存討論的背后原理
    • 如果對任何數據類型的訪問是隨機的而沒有規律模式,那么緩存就沒有用了
  • 顛簸發生的原因:分配的頁框數<局部大小之和

工作集模型

  • ≡ 工作集窗口 ≡ 固定數目的頁的引用
  • WSSi(Pi進程的工作集) = 最近中所有頁的引用數目(隨時間變化)
    • 如果太小,那么它不能包含整個局部
    • 如果太大,那么它可能包含多個局部
    • 如果 = ∞,那么工作集合為進程執行所接觸到的所有頁的集合
  • D = ∑WSSi ≡ 總的幀需求量
  • 如果D>m ⇨ 抖動(顛簸)
  • 策略:如果D>m,則暫停一個進程
  • 優點:可防止抖動,同時保持盡可能高的多道程序,優化了CPU利用率
  • 困難:跟蹤工作集
    • 工作集窗口是一個移動窗口。對於每次內存引用,新的引用出現在一端,最舊的引用離開另一端。如果一個頁面在工作集窗口內的任何位置被引用過,那么它就在工作集窗口中
    • 通過定期時鍾中斷和引用位,能夠近似工作集模型


缺頁率(PFF)策略

  • 抖動具有高缺頁率,可以控制缺頁錯誤率來避免抖動
  • 設置可接收的缺頁率
    • 如果缺頁率太低,回收一些進程的頁框
    • 如果缺頁率太高,就分給進程一些頁框
  • 可能不得不換出一個進程。如果缺頁錯誤率增加並且沒有空閑幀可用,那么必須選擇某個進程並將其交換到后備存儲。然后,再將釋放的幀分配給具有高缺頁錯誤率的進程

內核內存分配

基本概念

  • 用戶態進程需要內存時,可以從空閑頁框鏈表中獲得空閑頁,這些頁通常是分散在物理內存中的,進程最后一頁可能產生內碎片
  • 內核內存的分配不同於用戶內存
  • 通常從空閑內存池中獲取,其原因是:
    • 內核需要為不同大小的數據結構分配內存,其中有的小於一頁。因此,內核應保守地使用內存,並努力最小化碎片消費
    • 一些內核內存需要連續的物理頁。然而,有的硬件設備與物理內存直接交互,即無法享有虛擬內存接口帶來地便利,因而可能要求內存常駐在連續物理內存中
  • 內核在使用內存塊時有如下特點
    • 內存塊的尺寸比較小
    • 占用內存塊的時間比較短
    • 要求快速完成分配和回收
    • 不參與交換
    • 頻繁使用尺寸相同的內存塊,存放同一結構的數據
    • 要求動態分配和回收

伙伴(Buddy)系統

  • 主要用於Linux早期版本中內核底層內存管理
  • 一種經典的內存分配方案
  • 從物理上連續的大小固定的段上分配內存
  • 主要思想:內存按2的冪的大小進行划分,即4KB、8KB等,組成若干空閑塊鏈表;查找鏈表找到滿足進程需求的最佳匹配塊
    • 滿足要求是以2的冪為單位的
    • 如果請求不為2的冪,則需要調整到下一個更大的2的冪
    • 當分配需求小於現在可用內存時,當前段就分為兩個更小的2的冪段,繼續上述操作直到合適的段大小
  • 算法
    • 首先將整個可用空間看作一塊:2n
    • 假設進程申請的空間大小為s,如果滿足 2n-1<s<=2n,則分配整個塊,否則將塊划分為兩個大小相等的伙伴,大小為2n-1
    • 一直划分下去直到產生大於或等於s的最小塊分配給進程
  • 優點
    • 可通過合並而快速形成更大的段
  • 缺點
    • 調整到下一個2的冪容易產生碎片

Slab分配

  • 內核分配的另一方案
  • Slab也稱為板塊,是由一個多個物理上連續的頁或內存卡組成,Slab中的內存塊需要一起建立或撤銷
  • 高速緩存Cache,又稱為板塊組,含有一個或多個Slab;系統具有多個Cache,分別對應多種尺寸和結構相同的內存塊
  • 每個內核數據結構都有一個Cache,如進程描述符、文件對象、信號量等
    • 每個Cache含有內核數據結構的對象實例。例如信號量Cache存儲着信號量對象,進程描述符Cache存儲着進程描述符對象
  • 當創建Cache時,包括若干個標記為空閑的對象,對象的數量與Slab的大小有關
    • 12KB的Slab(包括3個連續的頁)可以存儲6個2KB大小的對象。開始所有的對象都標記為空閑
    • 當需要內核對象時,可從cache上直接獲取,並標識對象為已使用
  • Slab有三種狀態:
    • 滿的:Slab中所有對象被標記為使用
    • 空的:Slab中所有對象被標記為空閑
    • 部分:Slab中有的對象被標記為使用,有的對象被標記為空閑
  • 當一個Slab充滿了已使用的對象時,下一個對象的分配從空閑的Slab開始分配
    • 如果沒有空閑的Slab,則從物理連續頁上分配新的Slab,並賦給一個Cache
  • 優點
    • 沒有因碎片而引起的內存浪費
      • 因為每個內核數據結構都有相應的Cache,而每個Cache都由若干Slab組成,每個Slab又分為若干與對象大小相同的部分
    • 內存請求可以快速滿足
      • 由於對象預先創建,所以可以快速分配,剛用完對象並釋放時,只需要標記為空閑並返回,以便下次使用
  • 簡單塊列表(SLOB):分配器用於有限內存的系統,例如嵌入式系統。SLOB工作采用3個對象列表:小(用於小於256字節的對象)
    、中(用於小於1024字節的對象)和大(用於小於頁面大小的對象)。內存請求采用首先適應策略,從適當大小的列表上分配對象

其他注意事項

預先調頁

  • 在進程啟動初期,減少大量的缺頁中斷
    • 例如:當重啟一個換出進程時,由於所有的頁都在磁盤上,每個頁都必須通過缺頁中斷調入內存
  • 在引用前,調入進程的所有或一些需要的頁面
    • 例如:對於采用工作集的系統,為每個進程保留一個位於其工作集內的頁的列表
  • 如果預調入的頁面沒有被使用,則內存被浪費
  • 在有些情況下,預調頁面可能具有優點。問題在於,采用預調頁面的成本是否小於處理相應缺頁錯誤的成本。通過預調頁面而調進內存的許多頁面也有可能沒有使用

頁面尺寸選擇

  • 頁面大小總是2的冪,通常是4KB-4MB
  • 頁表大小——需要大的頁
    • 對於給定的虛擬內存空間,降低頁大小,也就是增加了頁的數量,因此也增加了頁表大小
    • 因為每個活動進程必須有自己的頁表,所以期望大的頁面
  • 碎片——需要小的頁
    • 較小的頁可能更好的利用內存,最小化內部碎片
  • I/O開銷——需要大的頁
    • I/O時間包括尋道、延遲和傳輸時間,盡管傳輸時間和傳輸量(即頁的大小)成正比,需要小的頁,但是尋道時間和延遲時間遠遠超過傳輸時間
  • 程序局部——需要小的頁
    • 較小的頁允許每個頁更精確匹配程序局部,而采用較大的頁不但傳輸所需要的,還會傳輸在頁內的其他不需要使用的內容
    • 采用較小頁面,會有更好的精度,以允許只隔離實際需要的內存
    • 采用較大頁面,不僅必須分配並傳輸所需要的內容,而且還包括其他碰巧在頁面內的且並不需要的內容
    • 較小的頁面應導致更少的I/O和更少的總的分配內存
  • 缺頁次數——需要大的頁
    • 由於每個缺頁會產生大量的額外開銷,為了降低缺頁次數,需要較大的頁
  • 其他因素
    • 如頁大小和調頁設備的扇區大小的關系等
  • 沒有最佳答案,總的來說,趨向更大的頁

TLB范圍

  • TLB范圍——通過TLB所訪問的內存量
  • TLB范圍 = (TLB大小)×(頁大小)
  • 理想情況下,一個進程的工作集應存放在TLB中,否則會有大量的缺頁中斷
    • 如果把TLB條數加倍,那么TLB的范圍就加倍,但是對於某些使用大量內存的應用程序,這樣做可能不足以存放工作集
  • 增加頁的大小
    • 對於不需要大頁的應用程序而言,這將導致碎片的增加
  • 提供多種頁的大小
    • 這允許需要大頁的應用程序有機會使用大頁而不增加碎片的大
    • 要求操作系統(而不是硬件)來管理TLB
    • 通過軟件而不是硬件來管理TLB會降低性能。然而,命中率和TLB范圍的增加會提升性能
    • 最近的趨勢傾向於由軟件來管理TLB和由操作系統來支持不同大小的頁面

反向頁表

  • 反向頁表降低了保存的物理內存
  • 不再包括進程邏輯地址空間的完整信息
  • 為了提供這種信息,進程必須保留一個外部頁表
  • 外部頁表可根據需要換進或換出內存

I/O互鎖

  • 允許某些頁在內存中被鎖住
  • 為了防止I/O出錯,有兩種解決方案:
    • 不對用戶內存進行I/O,即I/O只在系統內存和I/O設備間進行,數據在系統內存和用戶內存間復制
    • 允許頁所在內存,鎖住的頁不能被置換,即正在進程I/O的頁面不允許被置換算法置換出內存,當I/O完成時,頁被解鎖

程序結構

  • 數據結構和程序結構可能影響系統性能
  • 其他因素(編譯器、載入器、程序設計語言)對調頁都有影響


免責聲明!

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



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