計算機組成原理 — 存儲系統

目錄

前文列表

《計算機組成的基本硬件設備》
《計算機組成原理 — 馮諾依曼體系結構》
《計算機組成原理 — 中央處理器》
《計算機組成原理 — 指令系統》node

存儲系統

存儲系統是指計算機中由存放程序和數據的各類存儲設備、控制部件及管理信息調度的設備(硬件)和算法(軟件)所組成的系統。程序員

基本術語web

  • 存儲器:存放程序和數據的器件。
  • 存儲位:存放一個二進制數位的存儲單元,是存儲器最小的存儲單位。
  • 存儲字:計算機可尋址(做爲一個總體存入或取出)的最小信息單位。
  • 存儲單元:存放一個存儲字的若干個記憶單元組成一個存儲單元。
  • 存儲體:大量存儲單元的集合組成存儲體。
  • 存儲單元地址:存儲單元的編號。
  • 字編址:對存儲單元按字編址。
  • 字節編址:對存儲單元按字節編址。
  • 尋址:經過地址尋找數據,從對應地址的存儲單元中訪存數據。

存儲器分類

首先咱們有必要來了解一下存儲器的分類。算法

按存儲介質分類

半導體存儲器:用半導體器件組成的存儲器。
磁表面存儲器:用磁性材料作成的存儲器。
激光存儲器:信息以刻痕的形式保存在盤面上,用激光束照射盤面,靠盤面的不一樣反射率來讀出信息。數據庫

按存取方式分類

隨機存儲器(RAM,Random Access Memory):可隨機訪問任意存儲單元,且存取時間與存儲單元的物理位置無關。主要充當高速緩衝存儲器和主存儲器。編程

  • 動態隨機存儲器(DRAM):只能將數據保持很短的時間。爲了保持數據,DRAM 使用電容存儲,因此必須隔一段時間刷新(Refresh)一次,若是存儲單元沒有被刷新,存儲的信息就會丟失,關機就會丟失數據。經常使用於主存儲器。
  • 靜態隨機存儲器(SRAM):所謂的 「靜態」,指這種存儲器只要保持通電,裏面儲存的數據就能夠恆常保持。相對之下,DRAM 裏面儲存的數據須要週期性地 Refresh。然而,當電力供應中止(關機)時,儲存的數據仍是會消失,因此也稱爲 Volatile Memory(易失性存儲器),這與在斷電後仍能儲存數據的 ROM 或閃存不一樣的。可是 SRAM 也有它的缺點,即它的集成度較低,功耗較 DRAM 大,相同容量的 DRAM 內存能夠設計爲較小的體積,可是 SRAM 卻須要很大的體積。一樣面積的硅片能夠作出更大容量的 DRAM,所以 SRAM 顯得更貴。經常使用於高速緩存。

串行訪問存儲器(SAS):在存儲器中只能按某種順序來進行存取,存取時間與存儲單元的物理位置有關。數組

  • 順序存取存儲器(SAM):是徹底的串行訪問,如:磁帶,讀寫時要待磁帶移動到合適位置以後才能順序讀寫。因爲價格便宜,主要用於數據備份容災系統。
  • 直接存取存儲器(DAM):是部分的串行訪問,如:HHD 機械硬盤,對信息的存取有兩步操做。首先,磁頭直接移動到目標區域(磁道),而後再從磁道的合適位置開始讀寫,它介於順序存取和隨機存取之間。主要用於輔助存儲器。

只讀存儲器(ROM):是一種只能讀不能寫入的存儲器,即預先一次性寫入的存儲器。一般用來存放固定不變的信息,如微程序控制存儲器。緩存

按信息的可保存性分類

非永久記憶(易失性)的存儲器:掉電後數據消失的存儲器,如:半導體讀/寫存儲器 RAM。
永久性記憶(非易失性)的存儲器:掉電後仍能保存信息的存儲器,如:磁性材料作成的存儲器以及半導體 ROM。數據結構

三層存儲結構

存儲系統的性能在計算機中具備很是重要的地位,主要由於:dom

  • 馮諾伊曼體系結構是建築在 「存儲程序」 概念的基礎上,訪存操做約佔中央處理器(CPU)時間的 70% 左右。
  • 存儲管理與組織的好壞影響到整機效率。
  • 現代的信息處理,如圖像處理、數據庫、知識庫、語音識別、多媒體等對存儲系統的要求很高。

計算機的主存儲器(內存)來存放當前正在執行的應用程序及數據,但主存儲器不能同時知足存取速度快、存儲容量大和成本低的要求,在計算機中必須有速度由慢到快、容量由大到小的多級層次存儲器,以最優的控制調度算法和合理的成本,構成具備性能可接受的存儲系統,這就是 「緩存-主存-輔存」 三層存儲結構。

其中,高速緩衝存儲器(Cache)用來改善主存儲器與中央處理器的速度匹配問題(主存和 CPU 一直保持了大約一個數量級的差距),高速緩存-主存層級擁有接近於緩存的速度和接近於主存的容量,解決了速度和成本之間的矛盾,主要用於提升 CPU 的訪問速度;而輔助存儲器(外存)則使用來存放當前不在運行的大量程序及數據,主存-輔存層級擁有接近於主存儲器的速度和接近於輔存儲器的容量,解決了大容量和低成本的需求,主要用於擴大存儲空間。

在這裏插入圖片描述

高速緩存

高速緩存一般爲半導體存儲器、靜態隨機存儲器(SRAM)和非永久性記憶存儲器。高速緩存的提出創建在著名的局部性原理之上,局部性原理表示:程序訪問的地址每每集中在存儲器邏輯地址空間很小的範圍以內。這是由於程序的地址分佈原本就是連續的,再加上循環程序流、子程序調用程序流的重複執行,因此程序的地址訪問天然就會相對的集中了。在主存和 CPU 之間設置了 Cache 以後,若是當前正在執行的程序和數據存放在 Cache 中,則當程序運行時沒必要再從主存儲器讀取指令和數據,訪問 Cache 便可。

Cache 的工做原理有三點值得咱們注意:

  1. 主存和 Cache 都會採用分字塊的方式進行管理,Cache 中保存的就是對應的主存字塊的一個副本。每個 Cache 字塊都會有一個標記位,用於表示當前字塊裏存放的是哪個內存字塊的副本。經過這個標準位,CPU 就能夠判斷出但願訪問的內存字塊是否已經存在於 Cache 中了。
  2. 當 Cache 已經用滿,但主存還將新的字塊調入 Cache 時,就會執行一次 Cache 字塊的替換。這種替換遵照必定的規律,最好使得被替換的字塊是下一個時間段內估計最小使用的,這種規則成爲替換策略或替換算法,有替換部件實現。
  3. 當程序對 Cache 的字塊執行寫入時,如何保證 Cache 字塊和內存字塊的一致性。一般的有兩種寫入方式:一個是先寫 Cache 字塊,待 Cache 字塊被替換出去時再一次性寫入內存字塊;在一個是在寫 Cache 字塊的同時也寫入內存字塊。

主存儲器

內存一般爲半導體存儲器、動態隨機存儲器(DRAM)和非永久記憶存儲器。具備兩個很是重要的特性:「可隨機訪問任意存儲單元」 和 「掉電即失去數據」,這些特性均服務於馮諾依曼體系 「存儲程序」 的核心理想。

主存儲器和 CPU 的鏈接是由總線來支持的,包括數據總線、地址總線和控制總線。CPU 經過 AR(地址寄存器)& AB(地址總線)、DR(數碼寄存器)& DB(數據總線)和主存進行數據傳輸。若 AR 爲 K 位字長,表示 CPU 的尋址寬度,即容許主存包含有 2**K 個可尋址存儲單位;若 DR 爲 n 位字長,則表示在一個存儲週期內,CPU 和主存之間經過總線進行 n 位數據傳輸。控制總線包括控制數據傳輸的讀(READ)、寫(WRITE)和表示存儲器功能完成的(READY)的三種控制線。

CPU 讀取主存數據:當 CPU 從存儲器讀取一個存儲字時,CPU 必須指定該存儲字的地址,將該地址送到 AR 再經 AB 送到存儲器,同時,CPU 經過控制線發送 READ 信號到存儲器。此後,CPU 等到存儲器經過控制線發來一個 READY 信號,表示已經完成了數據的讀,並將數據經 DB 放到 DR 上了,CPU 再從 DR 取出相應的數據。以此來完成了一個存儲器字的讀與取。

** CPU 存放數據到主存**:CPU 爲了存放一個字到存儲器,首先將存儲字在存儲器中的存放地址經過 AR 經 AB 發送到存儲器,並將存儲字放到 DR,同時發出一個 WRITE 信號到存儲器。此後,CPU 等到接收 READY 信號。存儲器會根據 AB 收到的地址來存放 DB 接收到的存儲字,而後經過 READY 控制線發送 READY 信號給 CPU 接收。以此完成了一個存儲字的存放。

可見,主存和 CPU 之間採用的是異步工做方式,以 READY 信號表示以此訪存操做的結束。
在這裏插入圖片描述

輔助存儲器

外存一般爲磁表面存儲器、串行訪問存儲器和永久性記憶存儲器,有磁盤存儲器和古老一些的磁帶已經光盤。磁盤存儲器有 HDD(Hard Disk Drive)和 HDC(Hard Disk Controller)組成,而當下比較時髦的 SSD(Solid-state Drive)是一種主要以閃存(NAND Flash)做爲永久性存儲器的計算機存儲設備,區別於以機械臂帶動磁頭轉動實現讀寫操做的磁盤存儲器,NAND 或者其餘固態存儲以電位高低或者相位狀態的不一樣記錄 0 和 1。輔存的只要技術指標有:存儲密度、存儲容量、尋址時間、數據傳輸率、誤碼率和價格。

值得注意的是,HDD 的存取時間爲毫秒(ms)級別,爲主存儲器的存取時間爲納秒(ns)級別,兩種的速度差異十分大。所以 I/O 系統成爲了整個計算機系統的瓶頸,因此爲了進一步拉近距離,在主存儲器和輔存儲器之間也引入了緩存層,即磁盤 Cache。磁盤 Cache 一樣基於局部性原理,對數據使用了預讀策略。如今的 HDD 一般會帶有高速緩存介質,而且一般也爲 SRAM 或 DRAM,容量爲十幾 MB 以上。但又由於 RAM 是易失性存儲器,因此爲了防止掉電時數據丟失的問題,一些 HDD 還會帶有不間斷電設備。

虛擬存儲器

在早期的計算機系統中,程序員會直接對主存儲器的物理地址進行操做,這種編程方式致使了當程序出現尋址錯誤時有可能會致使整個系統崩潰,當一個進程出現尋址錯誤時也可能會致使另外一個進程崩潰。顯然,直接操做主存的物理地址不是一個好的方法。並且,因爲不存在分頁或分段的存儲空間管理手段,因此 CPU 尋址寬度就成爲了存儲容量的限制,例如:32 位 CPU 只支持 4GB 內存的尋址。這致使了該計算機沒法運行存儲空間需求大於實際內存容量的應用程序。

爲了解決這些問題,現代計算機系統經過操做系統和硬件的結合,把主存儲器和輔存儲器從邏輯上統一成了一個總體,這就是虛擬存儲器,或稱爲虛擬存儲系統。虛擬存儲器是硬件異常、硬件地址翻譯、主存、磁盤文件和內核軟件的完美交互,它爲每一個進程提供了一個大的、一致的和私有的地址空間。

虛擬存儲器的兩大特色

  • 容許用戶程序使用比實際主存空間大得多的空間來訪問主存
  • 每次訪存都要進行虛實地址轉換。
    • 物理地址,即物理主存的地址空間。主存被組織成一個由 M 個連續的、字節大小的單元組成的數組,每字節都有一個惟一的物理地址(Physical Address,PA)。第一個字節的地址爲 0,接下來的字節的地址爲 1,依此類推。給定這種簡單的結構,CPU 訪問存儲器的最天然的方式就是使用物理地址,即物理尋址。
    • 虛擬地址,即虛擬存儲地址空間,它可以讓應用程序誤覺得本身擁有一塊連續可用的 「物理」 地址,但實際上從程序視角所見的都是虛擬地址,並且這些虛擬地址對應的物理主存空間一般多是碎片的,甚至有部分數據還可能會被暫時儲存在外部磁盤設備上,在須要時才進行數據交換。

虛擬存儲器的核心思路是根據程序運行時的局部性原理,一個程序運行時,在一小段時間內,只會用到程序和數據的很小一部分,僅把這部分程序和數據裝入主存便可,更多的部分能夠在須要用到時隨時從輔存調入主存。在操做系統和相應硬件的支持下,數據在輔存和主存之間按程序運行的須要自動成批量地完成交換。

虛擬存儲器提供了三個重要的能力

  1. 它將主存當作是一個存儲在磁盤上的地址空間的高速緩存,在主存中只保存活動區域,並根據須要在磁盤和主存之間來回傳送數據,經過這種方式,它高效地使用了主存。
  2. 它爲每一個進程提供了一致的地址空間,從而簡化了存儲器管理。
  3. 它保護了每一個進程的地址空間不被其餘進程破壞。

虛擬存儲器解決了三個根本需求

  1. 確保能夠運行存儲空間需求比實際主存儲容量大的應用程序
  2. 確保可執行程序被裝載後佔用的內存空間是連續的。由於 PC 程序計數器是自增的,換句話說就是程序執行必須順序存放在存儲器中,PC 纔可以按照程序語句,一條一條的讀取指令,不錯亂。
  3. 確保同時加載多個程序的時候不會形成內存地址衝突。雖然在生成可執行文件時指令已經有了對應的內存地址,但實際加載的時候,其實沒辦法保證程序必定就運行在這些內存地址上,由於多個程序同時運行的話,預期的內存地址極可能已經被其餘程序佔用了。

NOTE:虛擬存儲器指的是 「主存-輔存」 層次,由軟硬件結合實現,而 「緩存-主存」 是由存硬件實現的。

主存-輔存間信息交換單位和存儲管理方式

段式虛擬存儲管理方式

段式存儲管理是一種把主存按段分配的存儲管理方式,主存-輔存間信息傳送單位是不定長的段。優勢是段的分界與程序的天然分界是相對應的。例如:過程、子程序、數據表和陣列等待程序的模塊化性質均可以與段對應起來。因而段做爲獨立的邏輯單位能夠被其餘程序段調用,這樣就造成了段間鏈接,產生規模較大的程序。這樣的特性使得段易於編譯、管理、修改和保護,也便於多道程序共享。而缺點是容易在段間留下許多空餘的存儲空間碎片,且很差收集利用。除此以外,段式存儲管理還存在交換性能較低的問題。由於輔存的訪問速度比主存慢得多,而每一次交換,咱們都須要把一大段連續的內存數據寫到硬盤上,致使了當內存交換一個較大的段時,會讓機器顯得卡頓。
在這裏插入圖片描述

頁式虛擬存儲管理方式

頁式存儲管理是一種把主存按頁分配的存儲管理方式,主存-輔存間信息傳送單位是定長的頁。對比段而言,由於管理的粒度更細緻,因此形成內存頁碎片的浪費也會小不少。而缺點也正好相反,因爲頁不是程序獨立模塊對應的邏輯實體,因此處理、保護和共享都不及段來得方便。同時也由於頁要比段小得多,在 Linux 下一般默認設置爲 4KB,因此頁在進行交換時,不會出現段交換那般卡頓。因此,頁式存儲管理方式會更加的受到歡迎,Linux 操做系統採用的就是頁式存儲管理方式。

在這裏插入圖片描述

更進一步的,頁式存儲管理方式使得加載程序的時候,再也不須要一次性都把程序加載到內存中,而是在程序運行中須要用到的對應虛擬內存頁裏面的指令和數據時,再將其加載到內存中,這些操做由操做系統來完成。當 CPU 要讀取特定的頁,但卻發現頁的內容卻沒有加載時,就會觸發一個來自 CPU 的缺頁錯誤(Page Fault)。此時操做系統會捕獲這個錯誤,而後找到對應的頁並加載到內存中。經過這種方式,使得咱們能夠運行哪些遠大於實例物理內存的程序,但相對的執行效率也會有所降低。
在這裏插入圖片描述
經過虛擬存儲器、內存交換、內存分頁三個技術的結合。咱們最終獲得了一個不須要讓程序員考慮實際的物理內存地址、大小和當前分配空間的程序運行環境。這些技術和方式對於程序員和程序的編譯、連接過程而言都是透明的,印證了那句著名的話:全部計算機問題均可以經過插入一箇中間層來解決。

頁式虛擬存儲器

虛擬緩存器(Virtual Memory,VM)的實現思想就是將主存做爲輔存的緩存,使得計算機系統擁有了 「主存+輔存(交換空間)」 大小的存儲空間,同時也擁有了接近於主存的訪問速度。從概念上而言,虛擬存儲器被組織爲一個由存放在磁盤上 N 個連續的字節大小的單元組成的數組。每一個字節都有一個惟一的虛擬地址,這個惟一的虛擬地址做爲定向到數組的索引。磁盤上的數組的內容被緩存在主存中。和存儲器層次結構中其餘緩存同樣,磁盤上的數據被分割成塊,這些塊做爲磁盤(較低層)和主存(較高層)之間的傳輸單元。

虛頁與實頁

在頁式虛擬存儲器中,經過將虛擬存儲空間分割成爲了大小固定的虛擬頁(Vitual Page,VP),簡稱虛頁,每一個虛擬頁的大小爲 P=2^n 字節。相似地,物理存儲空間被分割爲物理頁(Physical Page,PP)也稱爲頁幀(Page Frame),簡稱實頁,大小也爲 P 字節。在任意時刻,虛擬頁面的集合都分爲三個不相交的子集:

  • 未分配的(unallocated):虛擬存儲系統還未分配(或建立)的頁。未分配的塊沒有任何數據和它們相關聯,所以也就不佔用任何磁盤空間。
  • 緩存的(cached):當前緩存在物理主存中的已分配頁。
  • 未緩存的(uncached):沒有緩存在物理主存中的已分配頁。

以下圖,虛擬頁 0 和 3 尚未被分配,所以在磁盤上還不存在。虛擬頁 一、4 和 6 被緩存在物理主存中。頁 二、5 和 7 已經被分配了,可是當前並未緩存在物理主存中,而是隻存在於磁盤中。

在這裏插入圖片描述

基於頁表的虛實地址轉換原理

同任何緩存設計同樣,虛擬存儲器系統必須有某種方法來斷定一個虛擬頁是否存放在物理主存的某個地方。若是存在,系統還必須肯定這個虛擬頁存放在哪一個物理頁中。若是物理主存不命中,系統必須判斷這個虛擬頁存放在磁盤的哪一個位置中,並在物理主存中選擇一個犧牲頁,而後將目標虛擬頁從磁盤拷貝到物理主存中,替換掉犧牲頁。這些功能是由許多軟硬件聯合提供,包括操做系統軟件,MMU(存儲器管理單元)地址翻譯硬件和一個存放在物理主存中的叫作頁表(Page Table)的數據結構,頁表將虛擬頁映射到物理頁。頁表的本質就是一個頁表條目(Page Table Entry,PTE)數組。

CPU 經過虛擬地址(Virtual Address,VA)來訪問存儲空間,這個虛擬地址在被送到存儲器以前須要先轉換成適當的物理地址。將一個虛擬地址轉換爲物理地址的任務叫作地址翻譯(Address Translation)。就像異常處理同樣,地址翻譯須要 CPU 硬件和操做系統之間的緊密合做。好比:Linux 操做系統的交換空間(Swap Space)。若是當 CPU 尋址時發現虛擬地址找不到對應的物理地址,那麼就會觸發一個異常並掛起尋址錯誤的進程。在這個過程當中,對其餘進程沒有任何影響。

虛擬地址與物理地址之間的轉換主要有 CPU 芯片上內嵌的存儲器管理單元(Memory Management Unit,MMU)完成,它是一個專用的硬件,利用存放在主存中的查詢表(地址映射表)來動態翻譯虛擬地址,該表的內容由操做系統管理。

在這裏插入圖片描述
因爲頁的大小爲 2 的整數次冪,因此頁的起點都落在低位字段爲 0 的地址上,能夠把虛擬地址分爲兩個字段,高位字段位虛擬頁號,低位字段爲虛頁內地址。在頁表中,對應每個虛頁號都有一個條目,格式爲 (虛頁號,實頁號,控制字)。
在這裏插入圖片描述
實頁號即爲實頁地址,被做爲物理地址的高字段,而物理地址的低字段則同爲虛擬地址的低字段(虛頁內地址)。拼接成爲了主存物理地址以後,就能夠據此訪問主存儲器數據了。
在這裏插入圖片描述
一般的,頁面中還包括有裝入位、修改位、替換控制位以及其餘保護位組成的控制字。e.g.

  • 裝入位爲 1:表示該條目對應的虛頁以及輔存調入主存;
  • 裝入位爲 0:表示對應的虛頁還沒有裝入主存,若是此時 CPU 訪問該頁就會觸發頁面失效中斷,啓動 I/O 子系統,根據外頁表項目中查找到的輔存地址,進行輔存到主存的頁面交換;
  • 修改位:表示主存頁面的內容是否被修改過,從主存交換到輔存時是否要寫回輔存。
  • 替換位:表示須要替換的頁。

應用 TLB 快表提高虛實地址轉換速度

當頁表已經存放在主存中,那麼當 CPU 訪問(虛擬)存儲器時,首先要查詢頁面獲得物理主存地址以後再訪問主存完成存取。顯然,地址轉換機制讓 CPU 多了一次訪問主存的操做,至關於訪問速度降低一半。並且當發生頁面失效時,還要進行主存-輔助的頁面交換,那麼 CPU 訪問主存的次數就更多了。爲了解決這個問題,在一些影響訪問速度的關鍵地方引入了硬件的支持。例如:採用按內容查找的相聯存儲器並行查找。此外,還進一步提出了 「快表」 的概念。把頁表中最活躍的部分存放在快速存儲器中組成快表,是減小 CPU 訪問時間開銷的一種方法。

快表由硬件(門電路和觸發器)組成,屬於 MMU 的部件之一,一般稱爲轉換旁路緩衝器(Translation lookaside buffer,TLB)。TLB 的本質也是一個 Cache,它比頁表小得多,通常在 16 個條目 ~ 128 個條目之間,快表只是頁表的一個小小的副本。查表時,帶着虛頁好同時差快表和慢表(原頁面),當在快表中找打條目時,則立刻返回主存物理地址到主存地址寄存器,並使慢表查詢做廢。此時,雖然使用了虛擬存儲器但實際上 CPU 訪問主存的速度幾乎沒有降低(CPU 再也不須要屢次訪問主存)。若是快表不命中,則須要花費一個訪主存時間查慢表,而後再返回主存物理地址到主存地址寄存器,並將此條目送入到快表中,替換到快表的某一行條目。

在這裏插入圖片描述

頁式虛擬存儲器工做的全過程

內頁表:虛擬地址與主存地址的映射。
外頁表:虛擬地址與輔存地址的映射。
虛地址格式:(虛頁號,虛頁內地址)
主存地址格式:(實頁號,實頁內地址)
輔存地址格式:(磁盤機號,磁頭號,柱面號,塊號,塊內地址)

從三種地址格式可見,虛地址-主存地址的轉換是虛實頁號替換,有內頁表完成;虛地址-輔存地址的轉換是虛頁號與 「磁盤機號,磁頭號,柱面號,塊號」 的替換,由外頁表完成。

在這裏插入圖片描述
一、2:虛擬存儲器每次訪問主存時都須要將多用戶虛地址轉換層主存實地址,這個由虛頁號轉換爲實頁號的內部地址轉換由內頁表來完成;
3:當對應內頁表條目的有效位爲 1 時,就按照物理主存地址 np 進行主存儲器訪問。
4:若是對應內存表條目的裝入位爲 0 時,表示該虛頁對應的實頁再也不主存中,那麼就觸發一個頁面失效中斷。有中斷處理器到輔存中調用對應的實頁。
5:到輔存中調頁,首先要進行外部地址轉換,查找外頁表,將多用戶虛擬地址轉換爲輔存實頁地址 Nvd。
6:根據輔存實頁地址 Nvd 到輔存中選頁。
7:將選中的輔存實頁通過 I/O 處理機送出到物理主存中。
9:此時還要肯定調入的輔存實頁應該放置到主存的什麼位置上,這經過查找實存頁面表來完成。
10:當主存對應的目標地址仍然空閒時,就會找到空頁面。
十一、12:但當主存已經裝滿時,就是執行頁面替換操做,由替換算法來決定替換哪個主存實頁到輔存中。
13:把待替換主存實頁放入 I/O 處理機,待替換主存頁是否被修改了是能夠經過頁表替換位知道的,此時若是待替換的主存實頁沒有被修改過,那麼是不須要回寫到輔存的。
14:但若是待替換的主存實頁被修改了,那麼就須要寫回輔存。
7:繼續將目標實頁寫入到物理主存中,完成替換。新頁調入主存時,須要修改相應的頁表條目。
8:若是待替換頁沒能裝入緩存,那麼還要繼續進入中斷,進行出錯處理或其餘處理。

大頁內存的性能問題

在頁式虛擬存儲器中,會在虛擬存儲空間和物理主存空間都分割爲一個個固定大小的頁,爲線程分配內存是也是以頁爲單位。好比:頁的大小爲 4K,那麼 4GB 存儲空間就須要 4GB/4KB=1M 條記錄,即有 100 多萬個 4KB 的頁。咱們能夠相待,若是頁過小了,那麼就會產生大量的頁表條目,下降了查詢速度的同時還浪費了存放頁面的主存空間;但若是頁太大了,又會容易形成浪費,緣由就跟段式存儲管理方式通常。因此 Linux 操做系統默認的頁大小就是 4KB,能夠經過指令查看:

$ getconf PAGE_SIZE
4096

但在某些對性能要求很是苛刻的場景中,頁面會被設置得很是的大,好比:1GB、甚至幾十 GB,這些頁被稱之爲 「大頁」(Huge Page)。大頁可以提高性能的主要緣由有如下幾點:

  • 減小頁表條目,加快檢索速度。
  • 提高 TLB 快表的命中率,TLB 通常擁有 16 ~ 128 個條目之間,也就是說當大頁爲 1GB 的時候,TLB 可以對應 16GB ~ 128GB 之間的存儲空間。

值得注意的是,首先使用大頁的同時通常會禁止主存-輔存頁面交換,緣由跟段式存儲管理方式同樣,大容量交換會讓輔存讀寫成爲 CPU 處理的瓶頸。再一個就是大頁也會使得頁內地址檢索的速度變慢,因此並不是是頁面的容量越大越好,而是須要對應用程序進行大量的測試取得頁面容量與性能的曲線峯值纔對。

Linux 的頁式虛擬存儲器系統

Linux 操做系統採用了頁式虛擬存儲器。Linux 上全部的進程都工做在一個 4G 的地址空間上,同時 Linux 會爲每一個進程維護一個單獨的虛擬地址空間。其中 0 ~ 3G 是應用進程能夠訪問的 User 地址空間,是某個進程獨有的,進程之間互相隔離;剩下 3G ~ 4G 是 Kernel 地址空間,全部進程都會共享這部分地址空間。因此,咱們習慣的將 Linux 虛擬存儲器系統分爲 「內核虛擬存儲器」 和 「進程虛擬存儲器」。

其中內核虛擬存儲器包含了內核的代碼和數據結構,內核虛擬存儲器的某些區域被映射到全部進程共享的物理主存頁面。例如,每一個進程共享內核的代碼和全局數據結構。內核虛擬存儲器的其餘區域包含每一個進程都不相同的數據。例如,頁表、內核在進程的上下文中執行代碼時使用的棧(內核棧),以及記錄虛擬地址空間當前組織的各類數據結構。
在這裏插入圖片描述

因爲每一個進程都有 3G 的私有進程空間,因此操做系統的物理內存沒法對這些地址空間進行一一映射,所以 Kernel 須要一種機制,把進程地址空間映射到物理內存上。當一個進程請求訪問內存時,操做系統經過存儲在 Kernel 中的進程頁表把這個虛擬地址映射到物理地址,若是尚未爲這個地址創建頁表項,那麼操做系統就爲這個訪問的地址創建頁表項。最基本的映射單位是 Page,對應的是頁表項 PTE。因爲頁表項和物理地址是多對一的關係,即多個頁表項能夠對應一個物理頁面,於是支持共享內存的實現(幾個進程同時共享物理內存)

在 Linux 操做系統使用文件或管道來進行進程之間的通訊會有不少侷限性,好比效率問題,以及數據處理使用文件描述符不如內存地址訪問來得方便,因而多個進程間以共享內存的方式進行通訊就成了一個不錯的選擇。Linux 在編程上爲咱們準備了多種手段的共享內存方案。包括:

  • mmap 內存共享映射。
  • XSI 共享內存。
  • POSIX 共享內存。

Linux 下的 KSM 內存頁共享的性能問題

KSM(Kernel Shared Memory)是 Linux Kernel 的一種內存共享機制,在 2.6.36 版本引入。簡而言之,KSM 用於合併具備相同內容的物理主存頁面以減小頁面冗餘。在 Kernel 中有一個 KSM 守護進程 ksmd,它會按期掃描用戶向它註冊的內存區域,尋找到相同的頁面就會將其合併,並用一個添加了寫保護的頁面來代替。當有進程嘗試寫入該頁面時,Kernel 會自動爲其分配一個新的頁面,而後將新數據寫入到這個新頁面,這就是典型的 COW 機制。相似的,存儲技術中有一個稱爲去耦合(de-duplication)的技術,經過刪除冗餘數據(基於數據塊,或者基於更大的數據片斷,好比文件)來減小已存儲的數據。公共數據片斷被合併(以一種 COW 方式),釋放空間供其餘用途。使用這種方法,存儲成本更低,最終須要的存儲器也更少。

KSM 最初被應用到 KVM 上,由於事實證實,若是虛擬化了許多相同的操做系統和應用程序組,那麼宿主機上許多內存頁面都是相同的。假如操做系統和應用程序代碼以及常量數據在 VMs 之間相同,那麼這個特色就頗有用。當頁面唯一時,它們能夠被合併,從而釋放內存,供其餘應用程序使用。將多個 VMs 具備的相同內存頁合併(共享),能夠騰出更多的可用物理內存。

在這裏插入圖片描述
可是事實上,KSM 能夠應用於任何應用。KSM 僅僅會合並匿名頁面,不會對文件映射的頁面作處理,通過 KSM 合併的頁面最初是被鎖定的內存中的,可是如今已經能夠像其餘頁面同樣被換出到交換區了。可是共享頁一經換出,其共享的特性就被打破,再次換入的時候,ksmd 必須從新對其處理。前面提到,KSM 僅僅會掃描那些向其註冊的區域,就是向 KSM 模塊註冊了若是條件容許能夠被合併的區域,經過 madvise 系統調用能夠作到這點 int madvise(addr, length, MADV_MERGEABLE)。同時,應用也能夠經過調用 int madvise(addr, length, MADV_UNMERGEABLE) 來取消這個註冊,從而讓頁面恢復私有特性。可是該調用可能會形成內存超額,形成 unmerge 失敗,很大程度上會形成喚醒 Out-Of-Memory killer,殺死當前進程。若是 KSM 沒有在當前運行的 Kernel 啓用,那麼前面提到的 madvise 調用就會失敗,若是內核配置了 CONFIG_KSM=y,調用通常是會成功的。

KSM 的管理和監控經過 sysfs(位於根 /sys/kernel/mm/ksm)執行。

  • pages_to_scan:定義一次給定掃描中能夠掃描的頁面數。
  • sleep_millisecs:定義執行另外一次頁面掃描前 ksmd 休眠的毫秒數。
  • max_kernel_pages:定義 ksmd 可使用的最大頁面數(默認值是可用內存的 25%,但能夠寫入一個 0 來指定爲無限)。
  • merge_across_nodes:控制不一樣 NUMA 節點內存的合併,若是被設置成 0,則只合並當前 NUMA 節點的內存。
  • run:控制 ksmd 的運行
    • 0 表示中止 ksmd,可是保持合併的頁面;
    • 1 表示運行 ksmd;
    • 2 表示中止 ksmd 並請求取消合併全部合併頁面。

KSM 合併效果會實時顯示在下面文件:

  • pages_shared:KSM 正在使用的不可交換的內核頁面的數量。
  • pages_sharing:一個內存存儲指示。
  • pages_unshared :爲合併而重複檢查的唯一頁面的數量。
  • pages_volatile:頻繁改變的頁面的數量。
  • full_scans:代表已經執行的全區域掃描的次數。

KSM 做者定義:較高的 pages_sharing/pages_shared 比率代表高效的頁面共享(反之則代表資源浪費)。

須要注意的是,應用 KSM 的時候要慎重考慮,由於 KSM 掃描相同的頁面的過程會消耗較多的 CPU 資源,在對虛擬機性能要求苛刻的環境中通常都會禁用 KSM。關閉 KSM,可讓做爲 Hypervisor 的 Linux Kernel(KVM)在負載增長時候,保證虛擬機的響應速度。這裏再次印證了一句名言:計算機藝術永遠是時間與空間的較量

經過動態連接來節省內存

從上文咱們知道,連接(Link)是程序被裝載到內存運行以前須要完成的一個步驟。連接又分爲動態連接(Dynamic Link)和靜態連接(Static Link)兩種方式。在動態連接的過程當中,咱們但願連接的不是存儲在磁盤上的目標文件代碼,而是連接到了內存中的共享庫(Shard Libraries)。這個加載到內存中的共享庫會被不少程序的指令調用。在 Windows 中,這個共享庫文件就是 .dll(Dynamic-Link Libary,動態連接庫)文件。而在 Linux 下,這些共享文件就是 .so(Shared Object)文件,咱們通常也稱之爲動態連接庫文件。在這裏插入圖片描述
不過,要想在程序中運行時加載共享庫代碼,就要求這些共享庫代碼是 「地址無關」 的。也就是說,咱們編譯出來的共享庫文件的指令代碼,是地址無關嗎。換句話說,共享庫不管加載到那個內存地址,都可以正常的運行。不然,就是地址相關代碼。幸運的是,大部分函數庫代碼都是能夠作到地址無關的,由於它們都被實現爲接收特定的輸入,進行肯定的操做,而後在返回結果。這些函數的代碼邏輯和輸入數據存放在內存什麼位置並沒有所謂。

有了動態連接方式以後,咱們得以把內存利用得更加的極致,動態連接庫是有如共享單車通常的存在。

相關文章
相關標籤/搜索