1. 程式人生 > >Linux下邏輯地址、線性地址、實體地址詳細總結

Linux下邏輯地址、線性地址、實體地址詳細總結

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

我們寫個最簡單的hello world程式,用gcc編譯,再反編譯後會看到以下指令:
  1. mov    0x80495b0, %eax
複製程式碼
這裡的記憶體地址0x80495b0 就是一個邏輯地址,必須加上隱含的DS 資料段的基地址,才能構成線性地址。也就是說 0x80495b0 是當前任務的DS資料段內的偏移。

 

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

Linux中邏輯地址等於線性地址。為什麼這麼說呢?因為Linux所有的段(使用者程式碼段、使用者資料段、核心程式碼段、核心資料段)的線性地址都是從 0x00000000 開始,長度4G,這樣 線性地址=邏輯地址+ 0x00000000,也就是說邏輯地址等於線性地址了。

這樣的情況下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 顯示當前暫存器的值:
  1. cs             0x73     115
  2. ss             0x7b     123
  3. ds             0x7b     123
  4. es             0x7b     123
複製程式碼
可以看到ds值為0x7b, 轉換成二進位制為 00000000 01111011,TI欄位值為0,表示使用GDT,GDT索引值為 01111,即十進位制15,對應的就是GDT內的__USER_DATA 使用者資料段描述符。
從上面可以看到,Linux在x86的分段機制上執行,卻通過一個巧妙的方式繞開了分段。Linux主要以分頁的方式實現記憶體管理。