1. 程式人生 > >Linux內存尋址之分段機制

Linux內存尋址之分段機制

tel 寄存器 操作 caption img 代碼段 window linux內存 -1

http://blog.xiaohansong.com/2015/10/03/Linux內存尋址之分段機制/

、段的起始地址、段的長度等等,而在保護模式下則復雜一些。IA32將它們結合在一起用一個8字節的數表示,稱為描述符 。
技術分享IA32的一個通用的段描述符的結構
從圖可以看出,一個段描述符指出了段的32位基地址和20位段界限(即段長)。這裏我們只關註基地址和段界限,其他的屬性略過。

段描述符表

各種各樣的用戶描述符和系統描述符,都放在對應的全局描述符表、局部描述符表和中斷描述符表中。描述符表(即段表)定義了IA32系統的所有段的情況。所有的描述符表本身都占據一個字節為8的倍數的存儲器空間,空間大小在8個字節(至少含一個描述符)到64K字節(至多含8K)個描述符之間。

  1. 全局描述符表(GDT)
    全局描述符表GDT(Global Descriptor Table),除了任務門,中斷門和陷阱門描述符外,包含著系統中所有任務都共用的那些段的描述符。 它的第一個8字節位置沒有使用。
  2. 中斷描述符表IDT(Interrupt Descriptor Table)
    中斷描述符表IDT(Interrupt Descriptor Table),包含256個門描述符。IDT中只能包含任務門、中斷門和陷阱門描述符,雖然IDT表最長也可以為64K字節,但只能存取2K字節以內的描述符,即256個描述符,這個數字是為了和8086保持兼容。
  3. 局部描述符表(LDT)
    局部描述符表LDT(local Descriptor Table),包含了與一個給定任務有關的描述符,每個任務各自有一個的LDT。 有了LDT,就可以使給定任務的代碼、 數據與別的任務相隔離。每一個任務的局部描述符表LDT本身也用一個描述符來表示,稱為LDT描述符,它包含了有關局部描述符表的信息,被放在全局描述符表GDT中。

總結

IA32的內存尋址機制完成從邏輯地址–線性地址–物理地址的轉換。其中,邏輯地址的段寄存器中的值提供段描述符,然後從段描述符中得到段基址和段界限,然後加上邏輯地址的偏移量,就得到了線性地址,線性地址通過分頁機制得到物理地址。
首先,我們要明確,分段機制是IA32提供的尋址方式,這是硬件層面的。就是說,不管你是windows還是linux,只要使用IA32的CPU訪問內存,都要經過MMU的轉換流程才能得到物理地址,也就是說必須經過邏輯地址–線性地址–物理地址的轉換。

Linux中分段的實現

前面說了那麽多關於分段機制的實現,其實,對於Linux來說,並沒有什麽卵用。因為,Linux基本不使用分段的機制,或者說,Linux中的分段機制只是為了兼容IA32的硬件而設計的。

Intel微處理器的段機制是從8086開始提出的, 那時引入的段機制解決了從CPU內部16位地址到20位實地址的轉換。為了保持這種兼容性,386仍然使用段機制,但比以前復雜得多。因此,Linux內核的設計並沒有全部采用Intel所提供的段方案,僅僅有限度地使用了一下分段機制。這不僅簡化了Linux內核的設計,而且為把Linux移植到其他平臺創造了條件,因為很多RISC處理器並不支持段機制。但是,對段機制相關知識的了解是進入Linux內核的必經之路。

從2.2版開始,Linux讓所有的進程(或叫任務)都使用相同的邏輯地址空間,因此就沒有必要使用局部描述符表LDT。但內核中也用到LDT,那只是在VM86模式中運行Wine,因為就是說在Linux上模擬運行Winodws軟件或DOS軟件的程序時才使用。

在 IA32 上任意給出的地址都是一個虛擬地址,即任意一個地址都是通過“選擇符:偏移量”的方式給出的,這是段機制存訪問模式的基本特點。所以在IA32上設計操作系統時無法回避使用段機制。一個虛擬地址最終會通過“段基地址+偏移量”的方式轉化為一個線性地址。 但是,由於絕大多數硬件平臺都不支持段機制,只支持分頁機制,所以為了讓 Linux 具有更好的可移植性,我們需要去掉段機制而只使用分頁機制。但不幸的是,IA32規定段機制是不可禁止的,因此不可能繞過它直接給出線性地址空間的地址。萬般無奈之下,Linux的設計人員幹脆讓段的基地址為0,而段的界限為4GB,這時任意給出一個偏移量,則等式為“0+偏移量=線性地址”,也就是說“偏移量=線性地址”。另外由於段機制規定“偏移量<4GB”,所以偏移量的範圍為0H~FFFFFFFFH,這恰好是線性地址空間範圍,也就是說虛擬地址直接映射到了線性地址,我們以後所提到的虛擬地址和線性地址指的也就是同一地址。看來,Linux在沒有回避段機制的情況下巧妙地把段機制給繞過去了。

另外,由於IA32段機制還規定,必須為代碼段和數據段創建不同的段,所以Linux必須為代碼段和數據段分別創建一個基地址為0,段界限為4GB的段描述符。不僅如此,由於Linux內核運行在特權級0,而用戶程序運行在特權級別3,根據IA32段保護機制規定,特權級3的程序是無法訪問特權級為0的段的,所以Linux必須為內核用戶程序分別創建其代碼段和數據段。這就意味著Linux必須創建4個段描述符——特權級0的代碼段和數據段,特權級3的代碼段和數據段。

總結

分段機制是IA32架構CPU的特色,並不是操作系統尋址方式的必然選擇。Linux為了跨平臺,巧妙的繞開段機制,主要使用分頁機制來尋址。

參考資料
《深入分析Linux內核源碼》

Linux內存尋址之分段機制