本文是
操作系統系列
第三篇,介紹物理內存管理。操作系統對內存的管理是非常複雜的,和程序的執行、硬件、編譯器等密切相關。本文從物理內存入手,介紹內存管理的重要概念,也爲後續的虛擬內存管理內容做鋪墊。原文鏈接,更多內容見公號機器學習與系統
,歡迎與我互動~
內存管理的需求
- 抽象,即給每個程序邏輯地址空間
- 保護,不同程序的地址空間互相隔離,無法越界訪問
- 共享,對於一些公共函數庫,可以只在內存中存一份,其它程序引用這一個庫即可
- 虛擬化,通過邏輯地址和虛擬內存,可以使用更大的地址空間
地址的概念
地址是用來標誌存儲資源位置的,在計算機中用一串二進制數據表示。
一. 地址空間
地址空間就是指地址的範圍,從最小值到最大值:
- 物理地址空間從0到物理內存的最大值:0~MAX_sys
- 邏輯地址空間從0到程序虛擬內存範圍的最大值:0~MAX_prog
下圖展示了物理地址空間,進程A、B的邏輯地址空間。
二. 地址生成
物理地址是已經確定的,邏輯地址的生成依賴於編譯器。
- 編譯:將高級語言編譯成彙編語言。假設此時此時地址已知,如果起始地址改變,必須重新編譯
- 彙編:將彙編語言翻譯長機器能夠識別的二進制代碼,裏面的地址是該程序執行時,對應地址空間中的位置
- 鏈接:將程序執行需要的函數庫鏈接到可執行文件中,更新地址空間
- 加載:將函數加載到內存中時根據程序塊在內存中的位置更新
邏輯地址空間
內的地址(重定位) - 執行:執行代碼時,程序在內存中可能會移動,這裏需要地址轉換(映射)支持
三. 地址解析
下圖是CPU和計算機的基本架構,我們以此圖來說明物理/邏輯地址在CPU和計算機中如何被解析處理的。
- 首先,CPU中的算數邏輯單元看到的都是邏輯地址
- 當CPU需要把數據寫入內存或從內存中讀取時,MMU會把邏輯地址轉換成對應的物理地址
- 控制邏輯把數據、操作請求和物理地址發送到總線,分爲讀請求和寫請求
- 寫請求,則把數據寫入內存
- 讀請求,則把數據從內存中讀取發送給CPU
在上面的過程中,MMU負責邏輯地址和物理地址之間的轉換,操作系統負責建立邏輯地址和物理地址之間的映射關係。
連續內存分配
基本概念
- 連續內存分配:給程序分配一塊連續內存區域
- 內存碎片:內存上一些沒有被分配利用的區域
- 內部碎片:某個程序分配的內存沒有充分利用。是否產生取決於分配算法,比如分配的內存大小是否要取整
- 外部碎片:被分配的內存區域之間沒的的空閒區域
- 碎片整理:通過調整進程佔用的內存區域位置來減少或避免分區碎片
- 碎片緊湊:通過移動分配給進程的內存區域,以合併外部碎片。要求運行的程序都可以動態重定位
動態分配
當程序被加載時,根據進程的實際需要動態分配內存空間,使分配的大小剛好與作業的大小相等。動態分區分配並不預先將內存劃分成一塊塊分區,而是在程序進入內存時,根據程序的大小動態地建立分區,因此係統中分區的大小是可變的,分區的數目也是可變的。
有以下三種分配策略:
-
最先匹配(First-fit):分配N個字節,
使用第一個可用空間比N大的內存塊
。如分配400 byte的內存塊,按照從上到下的查找順序,應該分配1K byte
內存區域。如果是從下往上查找,應該分配5K byte
的區域。 -
最佳匹配(Best-fit):分配N字節分區時,
查找並使用不小於N的最小空閒分區
。如果要分配2800 byte,應該分配3K byte
區域。 -
最差匹配(Worst-fit):分配N字節,
使用尺寸不小於N的最大空閒分區
。如果分配800 byte,則選擇5K byte
區域。
上述三種分區算法,在釋放分區時,都要檢查是否能和周圍的分區合併。
非連續內存管理
連續內存分配會出現內/外部碎片、動態修改比較困難、內存必須連續,而且內存利用率不高。因此提出了非連續內存分配的方法,允許程序使用非連續的內存空間、允許共享代碼和數據,以提高內存利用效率和管理的靈活性。
當然,這也帶來了挑戰:非連續內存分配中,如何有效實現和管理邏輯地址和物理地址間的映射。
下面介紹三種方式:
- 段式存儲管理(segmentation)
- 頁式存儲管理(paging)
- 段頁式存儲管理(上面兩者的綜合)
段式存儲管理
段(segment)指一類地址空間,一個段就是一個地址連續的內存塊,若干個段組成程序的邏輯地址空間。
每個段由0到最大的線性地址序列構成。各個段的長度可以是0到某個允許的最大值之間的一個數。不同的段的長度可以不同(通常情況下也都不一樣),段的長度在運行期間可以動態改變,比如push數據時,堆棧段的長度會增加,pop時會減少。段也可以被裝滿,但是通常情況下段的長度很大,這種情況很少發生。
段式存儲管理下的邏輯地址組成格式爲(s, o),s爲段號
,o爲段內偏移量
,段號和對應內存中的物理起始地址由段表
記錄。尋址時,先根據段號到段表中查到物理起始地址(基址),然後加上偏移量,得到最終的物理地址。
頁式存儲管理
頁式存儲管理有兩個至關重要的概念:
- 物理頁幀(Frame | Page Frame | 幀 | 頁幀):把物理地址空間分成大小相同的基本單位。大小爲2^n,如512/4096等。
- 邏輯頁面(Page | 頁):把邏輯地址空間劃分爲相同大小的基本單位
- 頁幀大小和頁面大小必須一致
頁式存儲管理的尋址方式和段式管理類似,邏輯地址格式爲(p, o),表示頁中的地址,其中p表示頁號,o表示偏移量。物理地址格式爲(f, o),表示頁幀中的地址,其中f表示頁幀號,o表示偏移量,頁偏移量和頁幀偏移量是相等的。
頁和頁幀的對應關係使用**頁表(Page Table)**來管理。尋址時首先根據頁號找到頁表中對應的頁幀號,然後用得到的頁幀號與偏移量組成實際的物理地址。
頁面和頁幀的大小相比分段要小得多,假設系統是32位,頁幀大小1024字節,這樣有232/210=2^22條頁表記錄,查詢頁表的時間要多很多。下面介紹兩個提高性能的方法:
- 使用快表(Translation Look-aside Buffer, TLB):直譯爲旁路快表緩衝,可以理解爲頁表緩衝。即在內存和CPU之間搭建頁表緩存,尋址時先到TLB中查找,未命中再到內存中的快表查找
- 多級頁表:(p1, p2, o)是兩級頁表的虛擬地址表示,先根據p1查找頁表1中的p2,再根據p2查詢真正的頁幀號,然後根據偏移地址o查到最終的物理地址
分段和分頁的比較
分頁和分段系統有許多相似之處。兩者都採用離散分配方式,且都要通過地址映射機構來實現地址變換。但在概念上兩者完全不同,主要表現在下述三個方面:
- 頁幀是信息的物理單位,分頁是爲了實現非連續分配,以便解決內存碎片問題, 提高內存的利用率。段是信息的邏輯單位,分段的目的是爲了能更好地滿足用戶的需要。
- 頁的大小固定且由系統決定,由系統把邏輯地址劃分爲頁號和頁內地址兩部分,是由機器硬件實現的,因而在系統中只能有一種大小的頁面。而段的長度卻不固定,決定於用戶所編寫的程序,通常由編譯程序在對源程序進行編譯時,根據信息的性質來劃分。
段式存儲和頁式存儲都是爲了更好管理內存,段式從程序的角度入手,頁式從物理底層的角度入手,在理解上,可以結合兩者的優缺點進行選擇:
分段 | 分頁 | |
---|---|---|
優點 | 段長可動態修改,方便編程,分段共享,分段保護,動態鏈接,動態增長 | 非連續分配,減少內存碎片,提高內存利用效率 |
缺點 | 內部碎片,地址計算需要更多硬件支持 | 需要兩次內存訪問,頁表可能很大 |
段頁式存儲管理
段頁式存儲管理充分利用了段式存儲在內存保護方面有優勢,頁式存儲在內存利用和優化轉移到後備存儲方面有優勢。
在段式存儲管理基礎上,給每個段加一級頁表。邏輯地址格式爲(s, p, o),s爲段號,p爲頁號,o爲頁內偏移。尋址時,現根據段號s查找段表中的頁表地址,然後到頁表中查找p對應的起始地址,最後加上偏移o得到最終的物理地址。
總結
程序在執行時,CPU看到的是邏輯地址,當CPU讀寫數據時,由MMU根據邏輯地址找到對應的物理地址,然後到總線上讀寫數據。通過這種管理機制,可以更好地管理內存,在多道程序執行中做到隔離和共享。
文章持續更新,可以微信搜索「 機器學習與系統 」閱讀最新內容,回覆【內推】【考研】獲取我準備的頭條內推以及考研信息