您好,登錄后才能下訂單哦!
這篇文章主要介紹“Linux內存尋址的分段機制原理是什么”,在日常操作中,相信很多人在Linux內存尋址的分段機制原理是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Linux內存尋址的分段機制原理是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一、前言
最近在學習Linux內核,讀到《深入理解Linux內核》的內存尋址一章。原本以為自己對分段分頁機制已經(jīng)理解了,結果發(fā)現(xiàn)其實是一知半解。于是,查找了很多資料,最終理順了內存尋址的知識。現(xiàn)在把我的理解記錄下來,希望對內核學習者有一定幫助,也希望大家指出錯誤之處。
二、分段到底是怎么回事
相信學過操作系統(tǒng)課程的人都知道分段分頁,但是奇怪的是書上基本沒提分段分頁是怎么產(chǎn)生的,這就導致我們知其然不知其所以然。下面我們先扒一下分段機制產(chǎn)生的歷史。
實模式的誕生(16位處理器及尋址)
在8086處理器誕生之前,內存尋址方式就是直接訪問物理地址。8086處理器為了尋址1M的內存空間,把地址總線擴展到了20位。但是,一個 尷尬的問題出現(xiàn)了,ALU的寬度只有16位,也就是說,ALU不能計算20位的地址。為了解決這個問題,分段機制被引入,登上了歷史舞臺。
為了支持分段,8086處理器設置了四個段寄存器:CS,DS,SS, ES。每個段寄存器都是16位的,同時訪問內存的指令中的地址也是16位的。但是,在送入地址總線之前,CPU先把它與某個段寄存器內的值相加。這里要注 意:段寄存器的值對應于20位地址總線中的高16位,所以相加時實際上是內存總線中的高12位與段寄存器中的16位相加,而低4位保留不變,這樣就形成一 個20位的實際地址,也就實現(xiàn)了從16位內存地址到20位實際地址的轉換,或者叫“映射”。
保護模式的誕生(32位處理器及尋址)
80286處理器的地址總線為24位,尋址空間達16M,同時引入了保護模式(內存段的訪問受到限制)
80386處理器是一個32位處理器,ALU和地址總線都是32位的,尋址空間達 4G。也就是說它可以不通過分段機制,直接訪問4G的內存空間。雖然它是新時代的小王子,超越它的無數(shù)前輩,然而,它需要背負家族的使命—兼容前代的處理 器。也就是說,它必須支持實模式和保護模式。所以,80386在段寄存器的基礎上構筑保護模式,并且保留16位的段寄存器。
從80386之后的處理器,架構基本相似,統(tǒng)稱為IA32(32 Bit Intel Architecture)。
三、IA32的內存尋址機制
尋址硬件
在 8086 的實模式下,把某一段寄存器左移4位,然后與地址ADDR相加后被直接送到內存總線上,這個相加后的地址就是內存單元的物理地址,而程序中的這個地址就叫 邏輯地址(或叫虛地址)。在IA32的保護模式下,這個邏輯地址不是被直接送到內存總線而是被送到內存管理單元(MMU)。MMU由一個或一組芯片組成, 其功能是把邏輯地址映射為物理地址,即進行地址轉換,如圖所示。
IA32的三種地址
邏輯地址:機器語言指令仍用這種地址指定一個操作數(shù)的地址或一條指令的地址。 這種尋址方式在Intel的分段結構中表現(xiàn)得尤為具體,它使得MS-DOS或Windows程序員把程序分為若干段。每個邏輯地址都由一個段和偏移量組成。
線性地址:線性地址是一個32位的無符號整數(shù),可以表達高達232(4GB)的地址。通常用16進制表示線性地址,其取值范圍為0x00000000~0xffffffff。
物理地址:也就是內存單元的實際地址,用于芯片級內存單元尋址。 物理地址也由32位無符號整數(shù)表示。
MMU地址轉化過程
MMU是一種硬件電路,它包含兩個部件,一個是分段部件,一個是分頁部件,在此,我們把它們分別叫做分段機制和分頁機制,以利于從邏輯的角度來理解硬件的實現(xiàn)機制。分段機制把一個邏輯地址轉換為線性地址;接著,分頁機制把一個線性地址轉換為物理地址。
MMU_translate
IA32的段寄存器
IA32中有六個16位段寄存器:CS,DS,SS,ES,F(xiàn)S,GS。跟8086的段寄存器不同的是,這些寄存器存放的不再是某個段的基地址,而是某個段的選擇符(Selector)。
四、分段機制的實現(xiàn)
段是虛擬地址空間的基本單位,分段機制必須把虛擬地址空間的一個地址轉換為線性地址空間的一個線性地址。
為了實現(xiàn)這種映射,僅僅用段寄存器來確定一個基地址是不夠的,至少還得描述段的長度,并且還需要段的一些其他信息,比如訪問權之類。所以,這里需要的是一個數(shù)據(jù)結構,這個結構包括三個方面的內容:
1.段的基地址(Base Address):在線性地址空間中段的起始地址。
2.段的界限(Limit):在虛擬地址空間中,段內可以使用的最大偏移量。
3.段的保護屬性(Attribute):表示段的特性。例如,該段是否可被讀出或寫入,或者該段是否作為一個程序來執(zhí)行,以及段的特權級等等。
上面的數(shù)據(jù)結構我們稱為段描述符,多個段描述符組成的表稱為段描述符表
段描述符
所謂描述符(Descriptor),就是描述段的屬性的一個8字節(jié)存儲單元。在實模式下,段的屬性不外乎是代碼段、堆棧段、數(shù)據(jù)段、段的起始地址、段的長度等等,而在保護模式下則復雜一些。IA32將它們結合在一起用一個8字節(jié)的數(shù)表示,稱為描述符 。
從圖可以看出,一個段描述符指出了段的32位基地址和20位段界限(即段長)。這里我們只關注基地址和段界限,其他的屬性略過。
1.段描述符表
各種各樣的用戶描述符和系統(tǒng)描述符,都放在對應的全局描述符表、局部描述符表和中斷描述符表中。描述符表(即段表)定義了IA32系統(tǒng)的所有段 的情況。所有的描述符表本身都占據(jù)一個字節(jié)為8的倍數(shù)的存儲器空間,空間大小在8個字節(jié)(至少含一個描述符)到64K字節(jié)(至多含8K)個描述符之間。
2.全局描述符表(GDT)
全局描述符表GDT(Global Descriptor Table),除了任務門,中斷門和陷阱門描述符外,包含著系統(tǒng)中所有任務都共用的那些段的描述符。 它的第一個8字節(jié)位置沒有使用。
3.中斷描述符表IDT(Interrupt Descriptor Table)
中斷描述符表IDT(Interrupt Descriptor Table),包含256個門描述符。IDT中只能包含任務門、中斷門和陷阱門描述符,雖然IDT表最長也可以為64K字節(jié),但只能存取2K字節(jié)以內的描述符,即256個描述符,這個數(shù)字是為了和8086保持兼容。
局部描述符表(LDT)
局部描述符表LDT(local Descriptor Table),包含了與一個給定任務有關的描述符,每個任務各自有一個的LDT。 有了LDT,就可以使給定任務的代碼、 數(shù)據(jù)與別的任務相隔離。每一個任務的局部描述符表LDT本身也用一個描述符來表示,稱為LDT描述符,它包含了有關局部描述符表的信息,被放在全局描述符表GDT中。
總結
IA32的內存尋址機制完成從邏輯地址—線性地址—物理地址的轉換。其中,邏輯地址的段寄存器中的值提供段描述符,然后從段描述符中得到段基址和段界限,然后加上邏輯地址的偏移量,就得到了線性地址,線性地址通過分頁機制得到物理地址。
首先,我們要明確,分段機制是IA32提供的尋址方式,這是硬件層面的。就是說,不管你是windows還是linux,只要使用IA32的CPU訪問內存,都要經(jīng)過MMU的轉換流程才能得到物理地址,也就是說必須經(jīng)過邏輯地址—線性地址—物理地址的轉換。
五、Linux中分段的實現(xiàn)
前面說了那么多關于分段機制的實現(xiàn),其實,對于Linux來說,并沒有什么卵用。因為,Linux基本不使用分段的機制,或者說,Linux中的分段機制只是為了兼容IA32的硬件而設計的。
Intel微處理器的段機制是從8086開始提出的, 那時引入的段機制解決了從CPU內部16位地址到20位實地址的轉換。為了保持這種兼容性,386仍然使用段機制,但比以前復雜得多。因此,Linux內 核的設計并沒有全部采用Intel所提供的段方案,僅僅有限度地使用了一下分段機制。這不僅簡化了Linux內核的設計,而且為把Linux移植到其他平 臺創(chuàng)造了條件,因為很多RISC處理器并不支持段機制。但是,對段機制相關知識的了解是進入Linux內核的必經(jīng)之路。
從2.2版開始,Linux讓所有的進程(或叫任務)都使用相同的邏輯地址空間,因此就沒有必要使用局部描述符表LDT。但內核中也用到LDT,那只是在VM86模式中運行Wine,因為就是說在Linux上模擬運行Winodws軟件或DOS軟件的程序時才使用。
在 IA32 上任意給出的地址都是一個虛擬地址,即任意一個地址都是通過“選擇符:偏移量”的方式給出的,這是段機制存訪問模式的基本特點。所以在IA32上設計操作 系統(tǒng)時無法回避使用段機制。一個虛擬地址最終會通過“段基地址+偏移量”的方式轉化為一個線性地址。 但是,由于絕大多數(shù)硬件平臺都不支持段機制,只支持分頁機制,所以為了讓 Linux 具有更好的可移植性,我們需要去掉段機制而只使用分頁機制。但不幸的是,IA32規(guī)定段機制是不可禁止的,因此不可能繞過它直接給出線性地址空間的地址。 萬般無奈之下,Linux的設計人員干脆讓段的基地址為0,而段的界限為4GB,這時任意給出一個偏移量,則等式為“0+偏移量=線性地址”,也就是說 “偏移量=線性地址”。另外由于段機制規(guī)定“偏移量<4GB”,所以偏移量的范圍為0H~FFFFFFFFH,這恰好是線性地址空間范圍,也就是說 虛擬地址直接映射到了線性地址,我們以后所提到的虛擬地址和線性地址指的也就是同一地址??磥?,Linux在沒有回避段機制的情況下巧妙地把段機制給繞過 去了。
另外,由于IA32段機制還規(guī)定,必須為代碼段和數(shù)據(jù)段創(chuàng)建不同的段,所以Linux必須為代碼段和數(shù)據(jù)段分別創(chuàng)建一個基地址為0,段界限為4GB 的段描述符。不僅如此,由于Linux內核運行在特權級0,而用戶程序運行在特權級別3,根據(jù)IA32段保護機制規(guī)定,特權級3的程序是無法訪問特權級為 0的段的,所以Linux必須為內核用戶程序分別創(chuàng)建其代碼段和數(shù)據(jù)段。這就意味著Linux必須創(chuàng)建4個段描述符——特權級0的代碼段和數(shù)據(jù)段,特權級3的代碼段和數(shù)據(jù)段。
六、總結
分段機制是IA32架構CPU的特色,并不是操作系統(tǒng)尋址方式的必然選擇。Linux為了跨平臺,巧妙的繞開段機制,主要使用分頁機制來尋址。
到此,關于“Linux內存尋址的分段機制原理是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。