linux記憶體定址解析__linux

來源:互聯網
上載者:User

1.記憶體位址

1.邏輯地址:每一個邏輯地址都有一個段和位移量組成。

2.線性地址:也叫虛擬位址,是一個32位不帶正負號的整數,可以用來表示高達4GB的地址,值得範圍從0x00000000到0xffffffff。

3.物理地址:用於記憶體晶片級記憶體單元定址

記憶體控制單元(MMU)將邏輯地址(通過段映射)轉化成線性地址,再(通過頁映射)轉化成物理地址。 2.硬體中的分段

1.段選擇符(段寄存器 )

   (1.包括:索引、TI、RPL

   (2.存放段描述符在段表中的索引號(段編號),TI標誌用於說明是是GDT還是LDT表,RPL標誌許可權。

2.段描述符:段描述符表中的一項,表示一段的資訊:

   (1.段的基地址(Base Address):線上性地址空間中段的起始地址。

   (2.段的界限(Limit):即段大小,在虛擬位址空間中,段內可以使用的最大位移量。

   (3.段的保護屬性(Attribute): 表示段的特性。例如,該段是否可被讀出或寫入,或者該段是否作為一個程式來執行,以及段的特權 級等等。

3.段描述符放在通用描述元表GDT,或局部描述符表LDT中。能夠儲存在GDT中的段描述符的最大數目是8191個。

4.段定址過程(得到線性地址)

   (1.判斷指令類型,確定使用哪個段寄存器

   (2.讀段寄存器的內容,找到存放段描述資訊的資料結構。(GDTR、LDTR)

①通過TI標誌,判斷本次操作所用段是到全域段描述表中找,還是到局部段描述表中找

②讀GDTR或LDTR寄存器中存放的地址,找到描述表的首地址

③根據段寄存器中記錄的索引號從描述表首址處位移,找到第n個描述符,既是要找的段資訊。

   (3.得到基地址

   (4.指令地址做位移,判斷是否長度越界

   (5.根據指令性質及段描述符中的存取權限判斷是否越權

   (6.將基地址與指令中的位移地址相加得到實際的記憶體位址,完成地址映射。

 


5.保護的表現

(1.界限保護

       越界判斷:段長參數

       特權指令:新增的GDTR或LDTR寄存器不存在與舊指令相容問題,訪問他們的指令設定為特權指令,段寄存器的訪問屬性仍然同以往一樣無特權,既保持了相容,又保證了程式無法故意修改段描述進行越界。

(2.許可權保護

      根據段的保護屬性判斷是否具有存取權限。如,唯讀段中不允許寫入。

      系統態、使用者態分離。段寄存器中的後兩位RPL表示要求者操作要求的特權級。GDTR或LDTR中的dpl欄位設定了段的存取權限。指令段rpl標誌要求的許可權應不低於dpl規定的層級。特權指令只能在系統態執行。 3.Linux中的分段

1.linux中的四個段

運行在使用者態的所有linux進程使用一對相同的段來對指令和資料定址,即使用者程式碼片段和使用者資料區段;運行在核心態的所有linux進程也使用一對相同的段來對指令和資料定址,即核心程式碼片段和核心資料區段。

Base

G

Limit

S

Type

DPL

D/B

P

使用者程式碼片段

0x00000000

1

0xfffff

1

10

3

1

1

使用者資料區段

0x00000000

1

0xfffff

1

2

3

1

1

核心程式碼片段

0x00000000

1

0xfffff

1

10

0

1

1

核心資料區段

0x00000000

1

0xfffff

1

2

0

1

1

註:Linux中只有4個記憶體段 


2.linux段機制

他們都是從0開始,即意味著使用者態和核心態下的所有進程使用相同的邏輯地址。因為段機制就是將邏輯地址轉化為線性地址,也就是說在linux中分段機制並沒有起到實際的作用,只是走一下過程,而linux下的邏輯地址就等於線性地址。

3.GDT和LDT

每個cpu對應一個GDT,所有的GDT都存放在cpu_gdt_table數組中,而所有GDT的地址和他們的大小被存放在cpu_gdt_descr數組中。每個GDT包含18個段描述符;其中14個是空的。

Linux中基本不使用LDT。 4.硬體中的分頁

為了效率起見,線性地址分成以固定長度為單位的組,稱為頁。

分頁單元把所有的RAM分成固定長度的頁框(也叫物理頁),每個頁框包含一個頁,也就是說一個頁框的長度與一個頁的長度一致。頁框是主存的一部分,也是一個儲存地區。頁只是一個資料區塊,可以存放在任何頁框或磁碟中。分頁單元把線性地址轉化成物理地址。

把線性地址映射到物理地址的資料結構稱為頁表。頁表放在主存中,並在啟用分頁單元之前必須由核心對頁表進行適當的初始化。

1.常規分頁:

分頁單元處理4KB的頁。32位的線性地址分成3個域:目錄(最高10位)、頁表(中間10位)、位移量(最低12位)。

每個活動的進程必須有一個分配給它的頁目錄。但沒有必要馬上為進程所有的頁表都分配RAM。正在使用的頁目錄的物理地址存在寄存器cr3中。線性地址中的目錄欄位決定頁目錄中的目錄項,而目錄項指向適當的頁表。地址的頁表欄位又決定頁表中的表項,而表項含有頁所在頁框的物理地址。位移量欄位決定頁框內的相對位置。每一頁含有4096個位元組。

頁目錄和頁表都可以多大1024項,所以一個頁目錄可以定址高達1024*1024*4096=2^32.這和32位地址所期望的一樣。

 

或者下圖:

 

2.擴充分頁:

它允許頁框大小為4MB。目錄欄位10位,位移量欄位為22位。

3.實體位址延伸(PAE)分頁機制:

32位的物理地址,由於使用者進程線性地址空間的需要,核心不能直接對1GB以上的RAM進行定址。而許多伺服器需要大於4GB的RAM來運行數以千計的進程。所以Intel將管教數增加到36,現在處理器的定址能力為2^36=64GB.

4.硬體快取:

快取單元插在分頁單元和記憶體之間。

在linux設定中,對於所有的頁框都啟用快取,對於寫操作總是採用回寫策略。

5.轉換後援緩衝器TLB:

除硬體緩衝外,還採用TLB的快取來用於加快線性地址的轉換。 5.Linux中的分頁

Linux分頁機制的作用:分頁機制是在段機制之後進行的,它進一步將線性地址轉換為物理地址。我們先來看看硬體構造:

80386使用4K位元組大小的頁,且每頁的起始地址都被4K整除。因此,早期80386把4GB位元組線性地址空間劃分為1M個頁面,採用了兩級表結構。


兩級表的第一級表稱為頁目錄,儲存在一個4K位元組的頁中,頁目錄表共有1K個表項,每個表項為4個位元組,線性地址最高的10位(22-31)用來產生第一級表索引,由該索引得到的表項中的內容定位了二級表中的一個表的地址,即下級頁表所在的記憶體塊號。


第二級表稱為頁表,儲存在一個4K位元組頁中,它包含了1K位元組的表項,每個表項包含了一個頁的物理地址。二級頁表由線性地址的中間10位(12-21)位進行索引,定位頁表表項,獲得頁的物理地址。頁物理地址的高20位與線性地址的低12位形成最後的物理地址。利用兩級頁錶轉換地址圖:


80x86的分頁機制由CR0中的PG位啟用。如PG=1,啟用分頁機制,並使用本節要描述的機制,把線性地址轉換為物理地址。如PG=0,禁用分頁機制,直接把前面段機制產生的線性地址當作物理地址使用。

80386使用4K位元組大小的頁。每一頁都有4K位元組長,並在4K位元組的邊界上對齊,即每一頁的起始地址都能被4K整除(物理地址最低12位為0)。因此,80386把4G位元組的線性地址空間,劃分為1G個頁面,每頁有4K位元組大小。

分頁機制通過把線性地址空間中的頁,重新置放到物理地址空間來進行管理,因為每個頁面的整個4K位元組作為一個單位進行映射,並且每個頁面都對齊4K位元組的邊界,因此,線性地址的低12位經過分頁機制直接地作為物理地址的低12位使用。

線性/物理地址的轉換,可將其意義擴充為允許將一個線性地址標記為無效,而不是實際地產生一個物理地址。有兩種情況可能使頁被標記為無效:其一是線性地址是作業系統不支援的地址;其二是在虛擬儲存空間系統中,線性地址對應的頁儲存在磁碟上,而不是儲存在RAM儲存空間中。在前一種情況下,程式因產生了無效地址而必須被終止。

對於後一種情況,該無效的地址實際上是請求作業系統的虛擬儲存管理系統,把存放在磁碟上的頁傳送到實體儲存體器中,使該頁能被程式所訪問。由於無效頁通常是與虛擬儲存系統相聯絡的,這樣的無效頁通常稱為未駐留頁,並且用頁表屬性位中叫做存在位的屬性位進行標識。未駐留頁是程式可訪問的頁,但它不在主儲存空間中。對這樣的頁進行訪問,形式上是發生異常,實際上是通過異常進行缺頁處理。

5.1 頁全域目錄

頁全域目錄表,最多可包含1024個頁目錄項,每個頁目錄項為4個位元組,算起來正好一個頁面,結構如圖所示:

·第0位是存在位,Present標誌:如果被置為1,所指的頁(或頁表)就在主存中;如果該標誌為0,則這一頁不在主存中,此時這個表項剩餘的位可由作業系統用於自己的目的。如果執行一個地址轉換所需的頁表項或頁目錄項中Present標誌被清0,那麼分頁單元就把該線性地址存放在控制寄存器cr2中,併產生14號異常:缺頁異常。(我們將在後面的一系列部落格中重點討論Linux如何使用這個欄位)。


·第1位是讀/寫位,第2位是使用者/管理員位,Read/Write標誌:含有頁或頁表的存取許可權(Read/Write或Read);User/Supervisor標誌:含有訪問頁或頁表所需的特權級。這兩位為頁目錄項提供硬體保護。當特權級為3的進程要想訪問頁面時,需要通過頁保護檢查,而特權級為0的進程就可以繞過頁保護,如圖所示:

·第3位是PWT(Page Write-Through)位,表示是否採用寫透方式,寫透方式就是既寫記憶體(RAM)也寫快取,該位為1表示採用寫透方式

·第4位是PCD(Page Cache Disable)位,表示是否啟用快取,該位為1表示啟用快取。

·第5位是訪問位,Accessed標誌:當對頁目錄項進行訪問時,A位=1。每當分頁單元對相應頁框進行定址時就設定這個標誌。當選中的頁被交換出去時,這一標誌就可以由作業系統使用。分頁單元從不重設這個標誌;而是必須由作業系統去做。

·第6位Dirty標誌,對於頁全域目錄項,其始終為1。
·第7位是Page Size標誌,只適用於頁目錄項。如果置為1,頁目錄項指的是4MB的頁面,請看後面的擴充分頁。
·第8位是Global 標誌:只應用於頁表項。這個標誌是在Pentium Pro引入的,用來防止常用頁從TLB快取中重新整理出去。只有在cr4寄存器的頁全域啟用(Page GlobalEnable ,PGE)標誌置位時這個標誌才起作用。

·第9~11位由作業系統專用,Linux也沒有做特殊之用

5.2 頁表

80386的每個頁目錄項指向一個頁表,頁表最多含有1024個頁面項,每項4個位元組,包含頁面的起始地址和有關該頁面的資訊。頁面的起始地址也是4K的整數倍,所以頁面的低12位也留作它用,如圖所示。

第31~12位是20位物理頁面地址,除第6位外第0~5位及9~11位的用途和頁目錄項一樣,第6位是頁面項專屬的,當對涉及的頁面進行寫操作時,D位被置1。

4GB的儲存空間只有一個頁目錄,它最多有1024個頁目錄項,每個頁目錄項又含有1024個頁面項,因此,儲存空間一共可以分成1024×1024=1M個頁面。由於每個頁面為4K個位元組,所以,儲存空間的大小正好最多為4GB。

5.3 線性地址到物理地址的轉換

當訪問一個操作單元時,如何由分段結構確定的32位線性地址通過分頁操作轉化成32位物理地址呢。過程如圖所示。

第一步,CR3包含著頁目錄的起始地址,用32位線性地址的最高10位A31~A22作為頁目錄的頁目錄項的索引,將它乘以4,與CR3中的頁目錄的起始地址相加,形成相應頁表的地址。

第二步,從指定的地址中取出32位頁目錄項,它的低12位為0,這32位是頁表的起始地址。用32位線性地址中的A21~A12位作為頁表中的頁面的索引,將它乘以4,與頁表的起始地址相加,形成32位頁面地址。

第三步,將A11~A0作為相對於頁面地址的位移量,與32位頁面地址相加,形成32位物理地址。

下面,我們就通過一個執行個體來介紹一下常規分頁是如何工作的。我們假定核心已給一個正在啟動並執行進程分配的線性地址空間範圍是0x20000000 到0x2003ffff(3GB線性地址空間是一個上限,使用者態進程只是引用其中的一個子集)。這個空間正好由64頁面組成。其實我們並不必關心包含這些頁的頁框的物理地址,為什麼呢。事實上,其中的一些頁甚至可能不在主存中。我們只關注頁表項中剩餘的欄位。
       進程的線性地址的最高10位開始。這兩個地址都以2開頭後面跟著0,因此高10位有相同的值,即0x080或十進位的128。因此,這兩個地址的頁目錄(Directory欄位)都指向進程頁目錄的第129項。相應的目錄項中必須包含分配給該進程的頁表的物理地址。如果沒有給這個進程分配其它的線性地址,頁目錄的其餘1023項都填為0。

 

    位的值(即Table欄位的值)範圍從0到0x03f,或十進位的從0到63。因而只有頁表的前64個表項是有意義的,其餘960表項都填0。
    需要讀線性地址0x20021406中的位元組。這個地址由分頁單元按下面的方法處理:
1. Directory欄位的0x80用於選擇頁目錄的第0x80目錄項,此目錄項指向和該進程的頁相關的頁表。
2. Table欄位0x21用於選擇頁表的第0x21表項,此表項指向包含所需頁的頁框。
3. 最後,Offet欄位0x406用於在目標頁框中讀位移量為0x406中的位元組。


    如果頁表第0x21表項的Present標誌為0,則此頁就不在主存中;在這種情況下,分頁單元線上性地址轉換的同時產生一個缺頁異常。無論何時,當進程試圖訪問限定在0x20000000到0x2003ffff範圍之外的線性地址時,都將產生一個缺頁異常,因為這些頁表項都填充了0,尤其是它們的Present標誌都被清0。

    當今,Linux採用了一種同時適用於32位和64位系統的普通分頁模型。前面我們看到,兩級頁表對32位系統來說已經足夠了,但64位系統需要更多數量的分頁層級。直到2.6.10版本,Linux採用三級分頁的模型。從2.6.11版本開始,採用了四級分頁模型:

如上圖所示:

圖中展示的4種頁表分別被稱作:
? 頁全域目錄(Page Global Directory)
? 頁上級目錄(Page Upper Directory)
? 頁中間目錄(Page Middle Directory)
? 頁表(Page Table)

     頁全域目錄包含若干頁上級目錄的地址,頁上級目錄又依次包含若干頁中間目錄的地址,而頁中間目錄又包含若干頁表的地址。每一個頁表項指向一個頁框。線性地址因此被分成五個部分。圖中沒有顯示位元,因為每一部分的大小與具體的電腦體繫結構有關。
     對於沒有啟用實體位址延伸的32位系統,兩級頁表已經足夠了。從本質上說Linux通過使“頁上級目錄”位和“頁中間目錄”位全為0,徹底取消了頁上級目錄和頁中間目錄欄位。不過,頁上級目錄和頁中間目錄在指標序列中的位置被保留,以便同樣的代碼在32位系統和64位系統下都能使用。核心為頁上級目錄和頁中間目錄保留了一個位置,這是通過把它們的頁目錄項數設定為1,並把這兩個目錄項映射到頁全域目錄的一個合適的目錄項而實現的。
     啟用了實體位址延伸的32 位系統使用了三級頁表。Linux 的頁全域目錄對應80x86 的頁目錄指標表(PDPT),取消了頁上級目錄,頁中間目錄對應80x86的頁目錄,Linux的頁表對應80x86的頁表。
    最終,64位系統使用三級還是四級分頁取決於硬體對線性地址的位的劃分。那麼,為什麼Linux是如此地熱衷使用分頁技術而對分段機製表現得那麼地冷淡呢,因為Linux的進程處理很大程度上依賴於分頁。事實上,線性地址到物理地址的自動轉換使下面的設計目標變得可行:
? 給每一個進程分配一塊不同的物理地址空間,這確保了可以有效地防止定址錯誤。
? 區別頁(即一組資料)和頁框(即主存中的物理地址)之不同。這就允許存放在某個頁框中的一個頁,然後儲存到磁碟上,以後重新裝入這同一頁時又被裝在不同的頁框中。這就是虛擬記憶體機制的基本要素。

每一個進程有它自己的頁全域目錄和自己的頁表集。當發生進程切換時,Linux把cr3控制寄存器的內容儲存在前一個執行進程的描述符中,然後把下一個要執行進程的描述符的值裝入cr3寄存器中。因此,當新進程重新開始在CPU上執行時,分頁單元指向一組正確的頁表。

把線性地址映射到物理地址雖然有點複雜,但現在已經成了一種機械式的任務。本章下面的幾節中列舉了一些比較單調乏味的函數和宏,它們檢索核心為了尋找地址和管理葉表所需的資訊;其中大多數函數只有一兩行。也許現在你就想跳過這部分,但是知道這些函數和宏的功能是非常有用的,因為在以後章節的討論中你會經常看到它們。

5.4 線性地址欄位處理

 

下列宏簡化了頁表處理:

PAGE_SHIFT

指定Offset欄位的位元;當用於80x86處理器時,它返回的值為12。由於頁內所有地址都必須放在Offset欄位, 因此80x86系統的頁的大小是212 =4096位元組。 PAGE_MASK宏產生的值為0xfffff000,用以屏蔽Offset欄位的所有位。

PMD_SHIFT

指定線性地址的Offset和Table欄位的總位元;換句話說,是頁中間目錄項可以映射的地區大小的對數。PMD_SIZE 宏用於計算由頁中間目錄的一個單獨表項所映射的地區大小,也就是一個頁表的大小。PMD_MASK宏用於屏蔽Offset欄位與Table欄位的所有位。當PAE 被禁用時,PMD_SHIFT 產生的值為22(來自Offset 的12 位加上來自Table 的10 位),PMD_SIZE 產生的值為222 或 4 MB, PMD_MASK產生的值為 0xffc00000。相反,當PAE被啟用時,PMD_SHIFT 產生的值為21 (來自Offset的12位加上來自Table的9位), PMD_SIZE 產生的值為221 或2 MB以及PMD_MASK產生的值為 0xffe00000。大型頁不使用最後一級頁表,所以產生大型頁尺寸的LARGE_PAGE_SIZE 宏等於PMD_SIZE(2PMD_SHIFT),而在大型頁地址中用於屏蔽Offset欄位和Table欄位的所有位的LARGE_PAGE_MASK宏,就等於PMD_MASK。

PUD_SHIFT
確定頁上級目錄項能映射的地區大小的對數。PUD_SIZE宏用於計算頁全域目錄中的一個單獨表項所能映射的地區大小。PUD_MASK宏用於屏蔽Offset欄位,Table欄位,Middle Air欄位和Upper Air欄位的所有位。在80x86處理器上,PUD_SHIFT總是等價於PMD_SHIFT,而PUD_SIZE則等於4MB或2MB。

PGDIR_SHIFT

確定頁全域頁目錄項能映射的地區大小的對數。 PGDIR_SIZE宏用於計算頁全域目錄中一個單獨表項所能映射地區的大小。PGDIR_MASK宏用於屏蔽Offset, Table,Middle Air及Upper Air的所有位。當PAE 被禁止時,PGDIR_SHIFT 產生的值為22(與PMD_SHIFT 和PUD_SHIFT 產生的值相同),PGDIR_SIZE 產生的值為 222 或 4 MB,以及 PGDIR_MASK 產生的值為 0xffc00000。相反,當PAE被啟用時,PGDIR_SHIFT 產生的值為30 (12位Offset 加 9 位Table再加 9位 Middle Air), PGDIR_SIZE 產生的值為230 或 1 GB以及PGDIR_MASK產生的值為0xc0000000。

PTRS_PER_PTE, PTRS_PER_PMD, PTRS_PER_PUD以及PTRS_PER_PGD

用於計算頁表、頁中間目錄、頁上級目錄和頁全域目錄表中表項的個數。當PAE被禁止時,它們產生的值分別為1024,1,1和1024。當<

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.