記憶體定址之段頁儲存機制分析

2021-10-05 22:22:58 字數 4185 閱讀 4502

學習作業系統這門課的時候,曾不止一次的接觸到作業系統的段頁式管理機制,但當是都是淺嘗輒止,不知道作業系統為啥要有這個機制。如今時間過去很久,關於這個機制的背後的原理和實現機制,早已忘記很久了。。最近在看作業系統方面的知識,藉此把自己的理解記錄一下。

要理解段頁式管理機制的發展歷程,還得從早期的處理器的定址方式說起。

首先簡單的介紹下記憶體定址的概念,現代計算機是基於馮.諾依曼的體系結構,這個體系結構是以儲存為中心的,也就是說所有的運算的前提都是先從記憶體中取得資料,所以記憶體定址技術從某種程度上代表了計算機技術。

直接定址

在處理器發展的早期階段,intel 公司推出了第一款8位的處理器–8080,它的記憶體定址的方式簡單粗暴,程式都是通過硬編碼的形式絕對定位到記憶體位址。這種情況下的程式都有明顯的缺點:可控性弱、難以重定位、難以維護等。

分段很快在 intel 推出的另一款處理器 8086 中,它可以定址空間達到 1m,即位址線擴充套件到了 20 位,由於當時製造20位的暫存器比較困難,為了能在 16 位的暫存器的基礎上定址 20 位的位址空間,引入了乙個重要的概念——,段的位址存放在暫存器中,換句話說把 1m 的空間分成數個 64k(16位暫存器可定址)的段來管理。所以8086中的分段機制的記憶體定址為:

1
段基址左移4位 + 偏移 = 實體地址

為了很好的支援分段機制,8086 處理器為程式使用的**段、資料段、堆疊段分別提供了專門的 16 位暫存器用於儲存這些段的段基址。引入了分段機制後,程式的位址不再需要硬編碼了,除錯錯誤也更加容易定位,同時更加重要的是支援了更大的記憶體位址空間。

分頁與虛擬記憶體

分頁機制第一次出現在 80386 的處理器中,這是記憶體定址方式的乙個重大突破,對後來的計算機系統產生了很大影響。80386處理器定址空間達到 32 位,是為了配合虛擬記憶體技術而引入了分頁機制,接下來簡單的說下虛擬記憶體技術。

簡單的來說,虛擬記憶體技術允許程式使用的記憶體大於計算機的實際記憶體。舉個簡單例子來說,執行乙個大型遊戲的時候,它需要 5g 記憶體,但是 32 位的筆記本最大支援 4g 記憶體,而馮.諾依曼體系告訴我們,程式執行的時候,需要將程式裝載到記憶體中才可以執行,那麼難道這個遊戲就玩不了了麼?不是的,借助虛擬記憶體技術就可以執行這個程式了,只不過可能會卡點。

虛擬記憶體技術本質上將部分硬碟空間對映到記憶體中的一種技術,這樣使得程式可以使用的記憶體空間變大了。簡單的做法是如果程式訪問的位址不在記憶體中時(放在外部磁碟空間中),就需要將訪問位址所對應磁碟空間的程式內容載入到記憶體中,在載入的過程中可能面臨著舊程式內容的置換。如何方便的管理這些置換或者載入的內容呢?作業系統引入了的概念,頁是乙個儲存單位,一般為4k 大小。

話說回來能通過段機制管理這些被置換和載入的內容麼?因為段的大小不定,因此不方便把整段大小的虛擬空間在記憶體和磁碟之間調來調去,需要乙個更小更靈活的儲存單位——頁,管理換進換出的機制稱為頁機制

引入分頁機制後,通過段機制轉換得到的位址僅僅是乙個中間位址——線性位址,線性位址還需要通過頁機制來轉變成實際的實體地址。總結一下記憶體定址過程,程式中使用的邏輯位址到最後的實體地址的轉換過程:

1
邏輯位址 -->(分段機制)  線性位址  -->(分頁機制) 實體地址

為了快速找到段選擇符所指定的段的位置,處理器提供了段暫存器(csssdsesfsgs),相比於 8086 來說,80386 新增了fsgs附加段暫存器,主要是為了減少es這個暫存器的負擔引入的。

值得注意的是cs暫存器,它含有乙個 2 位的字段,用以指定當前 cpu 的特權等級 dpl,3 為使用者態,0 為核心態。

段描述符

前面說到段選擇符是乙個 16 位的字段,格式如下:

段選擇符中的索引號可以索引到段的資訊,段的資訊是由8位元組的段描述符表示,段描述符存放在全域性描述符表(gdt)和區域性描述符表(ldt)中。同樣 gdt 和 ldt 的位址也是存放在暫存器中的,通常系統初始化的時候會初始化gdtr控制暫存器,寫入 gdt 表的位址,如果每個程序除了存放 gdt 外,還需要自己的附加的段,就可以建立乙個ldt,將ldt表的位址寫進ldtr暫存器中。

快速訪問段描述符

因為段暫存器中存放的只是段選擇符的位址,由段選擇符來索引實際的段描述符還需要查詢 gdt 或者 ldt 表。為了加速邏輯位址到線性位址的轉換過程,80x86提供了一種附加的非程式設計暫存器,即每當乙個段選擇符裝入暫存器時,相對應的段描述符就由記憶體裝入到這個非程式設計暫存器中。

這樣當針對那個段的邏輯位址轉換線性位址時,就不用訪問 gdt 或者 ldt 表,直接引用存放這個段描述符的非程式設計暫存器就好了。

分段機制下位址轉換的過程

linux中的分段

linux 以非常有限的方式使用分段,更偏向使用分頁的形式管理記憶體,因為:

在 linux 執行在 80x86 平台上使用到了分段機制,並通過定義四個巨集來定義相應的段選擇符

也就是說對核心**段定址時,只需要將 __kernel_cs 的值裝入cs段暫存器即可,其中cs段暫存器中的段選擇符所指定的 rpl 值,表明了當前程序是屬於使用者態還是核心態。

對於 gdt 表來說,在 linux 中每個 cpu 對應乙個 gdt 表,所有的 gdt 都存放在cpu_gdt_table中,而 gdt 的位址和它們的大小,則存放在cpu_gdt_descr陣列中,這些都將在系統啟動之前初始化。

前面提到分頁機制是為了配合作業系統中的虛擬記憶體管理而引入的,分頁機制目的是為了將線性位址轉換成實體地址。

基本概念

簡要講述一下頁框頁表的概念

頁表:用來將頁對映到具體的頁框中的資料結構

常規分頁

因為每個頁的大小為 4kb,為了對映 4gb 的物理空間,頁表中將會有 1mb(2^20) 的對映項,對應每個程式來都要儲存乙個這麼大的頁表項是難以接受的,所以引出了多級頁表的概念,也稱為頁目錄

也就是說線性位址的轉換過程需要兩步,首先是查詢頁目錄找到具體的頁表,然後查詢頁表,找到具體的頁。如果這個頁不存在 ram 中,即缺頁,還會引發缺頁中斷,申請調頁;如果存在 ram 就可以完成線性位址到具體實體地址的轉換了。

分頁機制中每個活動程序必須有乙個頁目錄,不必為程序的所有頁表都分配 ram,只有實際使用到時才分配,這樣才會更有效率,程序正在使用的頁目錄位址存放在cr3暫存器中。

線性位址字段分析

因為 directory 和 table 欄位都是 10 位大小,所以頁目錄項和頁表都多達 1024 項。頁目錄和頁表的資料結構類似,都包含著下面字段:

轉換後援緩衝器 tlb

除了內部的硬體快取記憶體外,還包含了乙個轉換後援緩衝器 tlb 的快取記憶體用以加快線性位址的轉換。當乙個線性位址第一次使用時,通過慢速訪問 ram 中的頁表計算出相應的實體地址,同時實體地址被存放到 tlb 的表項中,以便下次對同乙個實體地址的引用可以快速的得到轉換。

在多處理器中,每個 cpu 都有自己的 tlb。當 cpu 的cr3暫存器被修改時,那麼 tlb 的所有項都變得無效了,因為當前使用的是新的頁目錄了。

linux中的分頁

兩級頁表對 32 位系統是足夠了,但為了支援 64 位系統,還需要引入多級頁表,linux 從 2.6.11 版本開始引入四級頁表:

對於沒有啟用實體地址擴充套件的 32 位系統,兩級頁表已經足夠了,這時通過將頁上級目錄頁中間目錄位全部置0,從而保持了兩級頁表的格式。

記憶體定址二 分頁

分頁單元 paging unit 把線性位址轉換成實體地址。其中乙個關鍵任務是把所請求的訪問型別與線性位址的訪問許可權相比較,如果這次記憶體訪問時無效的,就產生乙個缺頁異常。為了效率起見,線性位址被分成以固定長度為單位的組,稱為頁 page 頁內部連續的線性位址被對映到連續的實體地址中。這樣,核心可...

4 6分段儲存管理方式 段頁式儲存管理方式

1.分段 3.段表與位址變換機構 段是連續存放在記憶體中。段表中針對每個 段編號 記錄 記憶體首位址 和 段長 同樣有兩次記憶體訪問問題 解決方法 設定聯想暫存器,用於儲存最近常用的段表項。位址變換機構 4.分頁和分段的主要區別 1 需求 分頁是出於系統管理的需要,是一種資訊的物理劃分單位,分段是出...

作業系統之段基址頁機制的開啟

一開始的段表便已經產生,我們編譯器寫的時候,將作業系統的核心放在4g的高位址0xc0000000處,但是我們作業系統的人員想將作業系統的核心放在低位址的地方,就是對應的0x000000000的地方,所以,需要我們手動調整,首先是載入段表的時候,需要我們手動調整段表載入的位址,我們手動將器減去0xc0...