操作系統是如何管理物理內存的?

本文是操作系統系列第三篇,介紹物理內存管理。操作系統對內存的管理是非常複雜的,和程序的執行、硬件、編譯器等密切相關。本文從物理內存入手,介紹內存管理的重要概念,也爲後續的虛擬內存管理內容做鋪墊。原文鏈接,更多內容見公號機器學習與系統,歡迎與我互動~

內存管理的需求

  1. 抽象,即給每個程序邏輯地址空間
  2. 保護,不同程序的地址空間互相隔離,無法越界訪問
  3. 共享,對於一些公共函數庫,可以只在內存中存一份,其它程序引用這一個庫即可
  4. 虛擬化,通過邏輯地址和虛擬內存,可以使用更大的地址空間

地址的概念

地址是用來標誌存儲資源位置的,在計算機中用一串二進制數據表示。

一. 地址空間

地址空間就是指地址的範圍,從最小值到最大值:

  • 物理地址空間從0到物理內存的最大值:0~MAX_sys
  • 邏輯地址空間從0到程序虛擬內存範圍的最大值:0~MAX_prog

下圖展示了物理地址空間,進程A、B的邏輯地址空間。

二. 地址生成

物理地址是已經確定的,邏輯地址的生成依賴於編譯器

  1. 編譯:將高級語言編譯成彙編語言。假設此時此時地址已知,如果起始地址改變,必須重新編譯
  2. 彙編:將彙編語言翻譯長機器能夠識別的二進制代碼,裏面的地址是該程序執行時,對應地址空間中的位置
  3. 鏈接:將程序執行需要的函數庫鏈接到可執行文件中,更新地址空間
  4. 加載:將函數加載到內存中時根據程序塊在內存中的位置更新邏輯地址空間內的地址(重定位)
  5. 執行:執行代碼時,程序在內存中可能會移動,這裏需要地址轉換(映射)支持

三. 地址解析

下圖是CPU和計算機的基本架構,我們以此圖來說明物理/邏輯地址在CPU和計算機中如何被解析處理的。

  1. 首先,CPU中的算數邏輯單元看到的都是邏輯地址
  2. 當CPU需要把數據寫入內存或從內存中讀取時,MMU會把邏輯地址轉換成對應的物理地址
  3. 控制邏輯把數據、操作請求和物理地址發送到總線,分爲讀請求和寫請求
    • 寫請求,則把數據寫入內存
    • 讀請求,則把數據從內存中讀取發送給CPU

在上面的過程中,MMU負責邏輯地址和物理地址之間的轉換,操作系統負責建立邏輯地址和物理地址之間的映射關係

連續內存分配

基本概念

  1. 連續內存分配:給程序分配一塊連續內存區域
  2. 內存碎片:內存上一些沒有被分配利用的區域
    • 內部碎片:某個程序分配的內存沒有充分利用。是否產生取決於分配算法,比如分配的內存大小是否要取整
    • 外部碎片:被分配的內存區域之間沒的的空閒區域
  3. 碎片整理:通過調整進程佔用的內存區域位置來減少或避免分區碎片
  4. 碎片緊湊:通過移動分配給進程的內存區域,以合併外部碎片。要求運行的程序都可以動態重定位

動態分配

當程序被加載時,根據進程的實際需要動態分配內存空間,使分配的大小剛好與作業的大小相等。動態分區分配並不預先將內存劃分成一塊塊分區,而是在程序進入內存時,根據程序的大小動態地建立分區,因此係統中分區的大小是可變的,分區的數目也是可變的。

有以下三種分配策略:

  1. 最先匹配(First-fit):分配N個字節,使用第一個可用空間比N大的內存塊。如分配400 byte的內存塊,按照從上到下的查找順序,應該分配1K byte內存區域。如果是從下往上查找,應該分配5K byte的區域。

  2. 最佳匹配(Best-fit):分配N字節分區時,查找並使用不小於N的最小空閒分區。如果要分配2800 byte,應該分配3K byte區域。

  3. 最差匹配(Worst-fit):分配N字節,使用尺寸不小於N的最大空閒分區。如果分配800 byte,則選擇5K byte區域。

上述三種分區算法,在釋放分區時,都要檢查是否能和周圍的分區合併。

非連續內存管理

連續內存分配會出現內/外部碎片、動態修改比較困難、內存必須連續,而且內存利用率不高。因此提出了非連續內存分配的方法,允許程序使用非連續的內存空間、允許共享代碼和數據,以提高內存利用效率和管理的靈活性。

當然,這也帶來了挑戰:非連續內存分配中,如何有效實現和管理邏輯地址和物理地址間的映射。

下面介紹三種方式:

  • 段式存儲管理(segmentation)
  • 頁式存儲管理(paging)
  • 段頁式存儲管理(上面兩者的綜合)

段式存儲管理

段(segment)指一類地址空間,一個段就是一個地址連續的內存塊,若干個段組成程序的邏輯地址空間。

每個段由0到最大的線性地址序列構成。各個段的長度可以是0到某個允許的最大值之間的一個數。不同的段的長度可以不同(通常情況下也都不一樣),段的長度在運行期間可以動態改變,比如push數據時,堆棧段的長度會增加,pop時會減少。段也可以被裝滿,但是通常情況下段的長度很大,這種情況很少發生。

段式存儲管理下的邏輯地址組成格式爲(s, o),s爲段號,o爲段內偏移量,段號和對應內存中的物理起始地址由段表記錄。尋址時,先根據段號到段表中查到物理起始地址(基址),然後加上偏移量,得到最終的物理地址。

頁式存儲管理

頁式存儲管理有兩個至關重要的概念:

  1. 物理頁幀(Frame | Page Frame | 幀 | 頁幀):把物理地址空間分成大小相同的基本單位。大小爲2^n,如512/4096等。
  2. 邏輯頁面(Page | 頁):把邏輯地址空間劃分爲相同大小的基本單位
  3. 頁幀大小和頁面大小必須一致

頁式存儲管理的尋址方式和段式管理類似,邏輯地址格式爲(p, o),表示中的地址,其中p表示頁號,o表示偏移量。物理地址格式爲(f, o),表示頁幀中的地址,其中f表示頁幀號,o表示偏移量,頁偏移量和頁幀偏移量是相等的。

頁和頁幀的對應關係使用**頁表(Page Table)**來管理。尋址時首先根據頁號找到頁表中對應的頁幀號,然後用得到的頁幀號與偏移量組成實際的物理地址。

該圖來自清華大學OS課程PPT,筆者做了適當修改

頁面和頁幀的大小相比分段要小得多,假設系統是32位,頁幀大小1024字節,這樣有232/210=2^22條頁表記錄,查詢頁表的時間要多很多。下面介紹兩個提高性能的方法:

  1. 使用快表(Translation Look-aside Buffer, TLB):直譯爲旁路快表緩衝,可以理解爲頁表緩衝。即在內存和CPU之間搭建頁表緩存,尋址時先到TLB中查找,未命中再到內存中的快表查找
  2. 多級頁表:(p1, p2, o)是兩級頁表的虛擬地址表示,先根據p1查找頁表1中的p2,再根據p2查詢真正的頁幀號,然後根據偏移地址o查到最終的物理地址

分段和分頁的比較

分頁和分段系統有許多相似之處。兩者都採用離散分配方式,且都要通過地址映射機構來實現地址變換。但在概念上兩者完全不同,主要表現在下述三個方面:

  1. 頁幀是信息的物理單位,分頁是爲了實現非連續分配,以便解決內存碎片問題, 提高內存的利用率。段是信息的邏輯單位,分段的目的是爲了能更好地滿足用戶的需要。
  2. 頁的大小固定且由系統決定,由系統把邏輯地址劃分爲頁號和頁內地址兩部分,是由機器硬件實現的,因而在系統中只能有一種大小的頁面。而段的長度卻不固定,決定於用戶所編寫的程序,通常由編譯程序在對源程序進行編譯時,根據信息的性質來劃分。

段式存儲和頁式存儲都是爲了更好管理內存,段式從程序的角度入手,頁式從物理底層的角度入手,在理解上,可以結合兩者的優缺點進行選擇:

分段 分頁
優點 段長可動態修改,方便編程,分段共享,分段保護,動態鏈接,動態增長 非連續分配,減少內存碎片,提高內存利用效率
缺點 內部碎片,地址計算需要更多硬件支持 需要兩次內存訪問,頁表可能很大

段頁式存儲管理

段頁式存儲管理充分利用了段式存儲在內存保護方面有優勢,頁式存儲在內存利用和優化轉移到後備存儲方面有優勢。

在段式存儲管理基礎上,給每個段加一級頁表。邏輯地址格式爲(s, p, o),s爲段號,p爲頁號,o爲頁內偏移。尋址時,現根據段號s查找段表中的頁表地址,然後到頁表中查找p對應的起始地址,最後加上偏移o得到最終的物理地址。

總結

程序在執行時,CPU看到的是邏輯地址,當CPU讀寫數據時,由MMU根據邏輯地址找到對應的物理地址,然後到總線上讀寫數據。通過這種管理機制,可以更好地管理內存,在多道程序執行中做到隔離和共享。

文章持續更新,可以微信搜索「 機器學習與系統 」閱讀最新內容,回覆【內推】【考研】獲取我準備的頭條內推以及考研信息

Reference

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章