邏輯位址,線性位址,實體地址

2021-06-13 21:45:34 字數 2767 閱讀 2878

邏輯位址轉線性位址

機器語言指令中出現的記憶體位址,都是邏輯位址,需要轉換成線性位址,再經過 mmu (cpu中的記憶體管理單元)轉換成實體地址才能夠被訪問到。

我們寫個最簡單的 hello world 程式,用 gcc 編譯,再反彙編後會看到以下指令:

mov 0x80495b0

,%eax

這裡的記憶體位址0x80495b0 就是乙個邏輯位址,必須加上隱含的 ds 資料段的基位址,才能構成線性位址。也就是說0x80495b0是當前任務的ds資料段內的偏移。

在x86保護模式下,段的資訊(段基線性位址、長度、許可權等)即段描述符佔8個位元組,段資訊無法直接存放在段暫存器中(段暫存器只有2位元組)。intel 的設計是段描述符集中存放在gdt或ldt中,而段暫存器存放的是段描述符在gdt或ldt內的索引值(index)。

linux中邏輯位址等於線性位址。為什麼這麼說呢?因為linux所有的段(使用者**段、使用者資料段、核心**段、核心資料段)的線性位址都是從0×00000000開始,長度4g,這樣線性位址=邏輯位址+ 0×00000000,也就是說邏輯位址等於線性位址了。

這樣的情況下linux只用到了gdt,不論是使用者任務還是核心任務,都沒有用到ldt。gdt的第12和13項段描述符是__kernel_cs和__kernel_ds,第14和15項段描述符是__user_cs和__user_ds。核心任務使用__kernel_cs和__kernel_ds,所有的使用者任務共用__user_cs和__user_ds,也就是說不需要給每個任務再單獨分配段描述符。核心段描述符和使用者段描述符雖然起始線性位址和長度都一樣,但dpl(描述符特權級)是不一樣的。__kernel_cs和__kernel_ds 的dpl值為0(最高特權),__user_cs和__user_ds的dpl值為3。

用gdb除錯程式的時候,用info reg顯示當前暫存器的值:

cs 0×73 115

ss 0x7b 123

ds 0x7b 123

es0x7b

123

可以看到ds值為0x7b,轉換成二進位制為00000000 01111011,ti字段值為0,表示使用gdt,gdt索引值為01111,即十進位制15,對應的就是gdt內的__user_ds使用者資料段描述符。

從上面可以看到,linux在x86的分段機制上執行,卻通過乙個巧妙的方式繞開了分段。

linux主要以分頁的方式實現記憶體管理。

圖1-2 邏輯位址轉線性位址

二、線性位址轉實體地址

前面說了linux中邏輯位址等於線性位址,那麼線性位址怎麼對應到實體地址呢?這個大家都知道,那就是通過分頁機制,具體的說,就是通過頁表查詢來對應實體地址。

準確的說分頁是cpu提供的一種機制,linux只是根據這種機制的規則,利用它實現了記憶體管理。

在保護模式下,控制暫存器cr0的最高位pg位控制著分頁管理機制是否生效,如果pg=1,分頁機制生效,需通過頁表查詢才能把線性位址轉換實體地址。如果pg=0,則分頁機制無效,線性位址就直接做為實體地址。

分頁的基本原理是把記憶體劃分成大小固定的若干單元,每個單元稱為一頁(page),每頁包含4k位元組的位址空間(為簡化分析,我們不考慮擴充套件分頁的情況)。這樣每一頁的起始位址都是4k位元組對齊的。為了能轉換成實體地址,我們需要給cpu提供當前任務的線性位址轉實體地址的查詢表,即頁表(page table)。注意,為了實現每個任務的平坦的虛擬記憶體,每個任務都有自己的頁目錄表和頁表。

為了節約頁表占用的記憶體空間,x86將線性位址通過頁目錄表和頁表兩級查詢轉換成實體地址。

32位的線性位址被分成3個部分:

最高10位directory頁目錄表偏移量,中間10位table是頁表偏移量,最低12位offset是物理頁內的位元組偏移量。

頁目錄表的大小為4k(剛好是乙個頁的大小),包含1024項,每個項4位元組(32位),專案裡儲存的內容就是頁表的實體地址。如果頁目錄表中的頁表尚未分配,則實體地址填0。

頁表的大小也是4k,同樣包含1024項,每個項4位元組,內容為最終物理頁的物理記憶體起始位址。

每個活動的任務,必須要先分配給它乙個頁目錄表,並把頁目錄表的實體地址存入cr3暫存器。頁表可以提前分配好,也可以在用到的時候再分配。

還是以mov 0x80495b0, %eax中的位址為例分析一下線性位址轉實體地址的過程。

前面說到linux中邏輯位址等於線性位址,那麼我們要轉換的線性位址就是0x80495b0。轉換的過程是由cpu自動完成的,linux所要做的就是準備好轉換所需的頁目錄表和頁表(假設已經準備好,給頁目錄表和頁表分配物理記憶體的過程很複雜,後面再分析)。

核心先將當前任務的頁目錄表的實體地址填入cr3暫存器。

線性位址0x80495b0 轉換成二進位制後是0000 1000 0000 0100 1001 0101 1011 0000,最高10位0000 1000 00的十進位制是32,cpu檢視頁目錄表第32項,裡面存放的是頁表的實體地址。線性位址中間10位00 0100 1001 的十進位制是73,頁表的第73項儲存的是最終物理頁的物理起始位址。物理頁基位址加上線性位址中最低12位的偏移量,cpu就找到了線性位址最終對應的物理記憶體單元。

我們知道linux中使用者程序線性位址能定址的範圍是0-3g,那麼是不是需要提前先把這3g虛擬記憶體的頁表都建立好呢?一般情況下,物理記憶體是遠遠小於3g的,加上同時有很多程序都在執行,根本無法給每個程序提前建立3g的線性位址頁表。linux利用cpu的乙個機制解決了這個問題。程序建立後我們可以給頁目錄表的表項值都填0,cpu在查詢頁表時,如果表項的內容為0,則會引發乙個缺頁異常,程序暫停執行,linux核心這時候可以通過一系列複雜的演算法給分配乙個物理頁,並把物理頁的位址填入表項中,程序再恢復執行。當然程序在這個過程中是被蒙蔽的,它自己的感覺還是正常訪問到了物理記憶體。

圖1-2 分頁機制

實體地址 虛擬位址 邏輯位址 線性位址

實際計算機的物理記憶體的位址,為32位或者64位。常見的記憶體條就是一類ram 隨機儲存儲存器,特點就是加電狀態下可任意讀寫,斷電後資訊消失 現代os都提供一技術 虛擬記憶體 virtual memory 它可以使給使用者錯覺好像自己在使用比實際物理記憶體大得多的記憶體,實際上通過對映把虛擬記憶體的...

邏輯位址和實體地址及線性位址

邏輯位址 logical address intel為了相容,將遠古時代的段式記憶體管理方式保留了下來。邏輯位址指的是機器語言指令中,用來指定乙個運算元或者是一條指令的位址。以上例,我們說的聯結器為a分配的0x08111111這個位址就是邏輯位址。不過不好意思,這樣說,好像又違背了intel中段式管...

線性位址和實體地址

在保護模式下32位 還是採用段機制訪問記憶體 初始化臨時的要進入到ia 32e模式的gdt資料結構 label gdt64 dq 0x0000000000000000 label desc code64 dq 0x0020980000000000 label desc data64 dq 0x000...