三種內(nèi)存地址

三種地址

今天我們來(lái)學(xué)習(xí)一下 Linux 下的內(nèi)存尋址,通常我們?cè)谡剝?nèi)存地址的時(shí)候,我們?cè)谡勈裁茨??所以首先我們得明確三種地址(以80x86微處理器為例):

  • 邏輯地址(logical address):機(jī)器語(yǔ)言指令中用來(lái)指定一個(gè)操作數(shù)或者一條指令的地址,每一個(gè)邏輯地址由一個(gè)段和偏移量組成,偏移量指明了從段開(kāi)始的地方到實(shí)際地址之間的距離
  • 線性地址(linear address 也叫做虛擬地址 virtual address):是一個(gè) 32 位無(wú)符號(hào)整數(shù),可以用來(lái)表達(dá) 4GB 的地址,通常用十六進(jìn)制數(shù)表示
  • 物理地址(physical address):用于內(nèi)存芯片內(nèi)的內(nèi)存單元尋址,它們從微處理器的地址引腳發(fā)送到內(nèi)存總線上的電信號(hào)對(duì)應(yīng)。

以上內(nèi)容來(lái)自 《Understanding The Linux Kernel》

內(nèi)存管理單元(Memory Management Unit, MMU)通過(guò)分段單元的把一個(gè)邏輯地址轉(zhuǎn)換成線性地址,通過(guò)分頁(yè)單元把線性地址轉(zhuǎn)換成物理地址。

從 80286 開(kāi)始,Intel 處理器以?xún)煞N不同的方式執(zhí)行地址轉(zhuǎn)換,分別為實(shí)模式(real mode)和保護(hù)模式(protected mode)。下面我們就展開(kāi)描述,在保護(hù)模式下,硬件的分段機(jī)制和分頁(yè)機(jī)制

分段機(jī)制

段選擇符和段寄存器

邏輯地址有兩部分組成:一個(gè)段標(biāo)識(shí)符和一個(gè)偏移量。短標(biāo)識(shí)符是一個(gè) 16 位的字段,成為段選擇符;偏移量是一個(gè) 32 位長(zhǎng)的字段。
為了快速找到段選擇符,處理器提供了段寄存器用來(lái)存放段選擇符,分別為 cs,ss,ds,es,fs,gs。
其中有三個(gè)有專(zhuān)門(mén)的用途:

  • cs:代碼段寄存器,指向包含程序指令的段
  • ss:棧段寄存器,指向包含當(dāng)前程序棧的段
  • ds:數(shù)據(jù)段寄存器,指向包含靜態(tài)數(shù)據(jù)或者全局?jǐn)?shù)據(jù)段
    其中,cs 含有一個(gè)兩位的字段,用來(lái)指明當(dāng)前的 CPU 特權(quán)等級(jí)(CPL),0 代表最高等級(jí)、3 代表最低等級(jí)。 Linux 只用到了 0 和 3,分別稱(chēng)為 內(nèi)核態(tài) 和 用戶(hù)態(tài)
段描述符

每個(gè)段由一個(gè) 8 字節(jié)的段描述符表示,描述了段的基本信息。段描述符放在全局描述符表(GDT)或者局部描述符表(LDT)中。
通常只會(huì)定義一個(gè) GDT,每個(gè)進(jìn)程除了放在 GDT 中的段以外,如還需要?jiǎng)?chuàng)建附加的段,就可以有自己的 LDT。GDT 在主存中的地址和大小存放在 gdtr 控制寄存器中,LDT 的地址和大小則存放在 ldtr 中。
段描述符包涵以下關(guān)鍵字段:

  • Base:包含段的首字節(jié)的線性地址
  • Type:描述了段的類(lèi)型特征和它的存取權(quán)限
  • DPL:限制對(duì)這個(gè)段的存取權(quán)限,表示訪問(wèn)這個(gè)段的要求的最小 CPU 特權(quán)等級(jí)
  • P:Segment-Present 標(biāo)志,表明當(dāng)前段是否在內(nèi)存中。Linux 總是把這個(gè)標(biāo)志設(shè)為 1,從來(lái)不會(huì)把整個(gè)段交換到磁盤(pán)上去
分段單元

介紹完上述的概念,那么邏輯地址是如何轉(zhuǎn)換到線性地址的呢?我們通過(guò)下面的步驟來(lái)簡(jiǎn)單說(shuō)明:

  • 先檢查段選擇符的 TI 字段,以決定段描述符保存在 GDT 中還是在 LDT 中。如果在 GDT 中,分段單元從 gdtr 中得到 GDT 的線性基地址,如果在 LDT 中,分段單元從 ldtr 中得到 LDT 的線性基地址
  • 從段選擇符的 index 字段計(jì)算段描述符的地址,計(jì)算方法為 index * 8 (一個(gè)段描述符為 8 字節(jié)),將這個(gè)結(jié)果與 gdtr 或者 ldtr 中的基地址相加
  • 把邏輯地址中的偏移量與段描述符中的 Base 值相加,就得到了線性地址
快速訪問(wèn)分段機(jī)制

如果每次都執(zhí)行上述的過(guò)程,可能會(huì)比較耗時(shí),因?yàn)?GDT 是存儲(chǔ)在主存中的,每次都訪問(wèn)主存,可能會(huì)比較慢,所以為了提高邏輯地址到線性地址的轉(zhuǎn)換速度,80x86 處理器提供了一組6個(gè)不可編程寄存器。每一個(gè)不可編程寄存器含有 8 個(gè)字節(jié)的段描述符,具體的值由相對(duì)應(yīng)的段寄存器中的段描述符確定。每當(dāng)一個(gè)段選擇符被裝入段寄存器,相對(duì)應(yīng)的段描述符就由主存裝入到對(duì)應(yīng)的不可編程寄存器,這樣就可以不需要上面三個(gè)過(guò)程中的前兩個(gè),就可以得到線性地址了。

分頁(yè)機(jī)制

頁(yè)、頁(yè)框和頁(yè)表

分頁(yè)單元把線性地址轉(zhuǎn)換成物理地址,其中的關(guān)鍵任務(wù)是把所請(qǐng)求的訪問(wèn)類(lèi)型與線性地址的訪問(wèn)權(quán)限做對(duì)比。

  • 頁(yè):為了更高效和更經(jīng)濟(jì)的管理內(nèi)存,線性地址被分為以固定長(zhǎng)度為單位的組,成為頁(yè)。頁(yè)內(nèi)部連續(xù)的線性地址空間被映射到連續(xù)的物理地址中。這樣,內(nèi)核可以指定一個(gè)頁(yè)的物理地址和對(duì)應(yīng)的存取權(quán)限,而不用指定全部線性地址的存取權(quán)限。這里說(shuō)頁(yè),同時(shí)指一組線性地址以及這組地址包含的數(shù)據(jù)
  • 頁(yè)框:分頁(yè)單元把所有的 RAM 分成固定長(zhǎng)度的頁(yè)框,每一個(gè)頁(yè)框包含一個(gè)頁(yè)。頁(yè)框是主存的一部分,因此也是一個(gè)存儲(chǔ)區(qū)域。頁(yè)和頁(yè)框相比,前者只是一個(gè)數(shù)據(jù)塊,可以存放在頁(yè)框或者磁盤(pán)中。
  • 頁(yè)表:把線性地址映射到物理地址的數(shù)據(jù)結(jié)構(gòu)成為頁(yè)表,頁(yè)表存放在主存中,并在啟用分頁(yè)單元之前必須由內(nèi)核對(duì)頁(yè)表進(jìn)行適當(dāng)?shù)某跏蓟?/li>
常規(guī)的分頁(yè)

從 80386 開(kāi)始,Intel 處理器的頁(yè)大小為 4KB。
32 位的線性地址被分為 3 個(gè)域:

  • Directory(目錄):最高 10 位
  • Table(頁(yè)表):中間 10 位
  • Offset(偏移量):最低 12 位
    線性地址的轉(zhuǎn)換分兩步完成,每一步都基于一種轉(zhuǎn)換表,第一種轉(zhuǎn)換表成為頁(yè)目錄表,第二種轉(zhuǎn)換表成為頁(yè)表。

為什么需要兩級(jí)呢?目的在于減少每個(gè)進(jìn)程頁(yè)表所需的 RAM 的數(shù)量。如果使用簡(jiǎn)單的一級(jí)頁(yè)表,將需要高達(dá) 2^20 個(gè)表項(xiàng)來(lái)表示每個(gè)進(jìn)程的頁(yè)表,即時(shí)一個(gè)進(jìn)程并不使用所有的地址,二級(jí)模式通過(guò)職位進(jìn)程實(shí)際使用的那些虛擬內(nèi)存區(qū)請(qǐng)求頁(yè)表來(lái)減少內(nèi)存容量。每個(gè)活動(dòng)的進(jìn)程必須有一個(gè)頁(yè)目錄,但是卻沒(méi)有必要馬上為所有進(jìn)程的所有頁(yè)表都分配 RAM,只有在實(shí)際需要一個(gè)頁(yè)表時(shí)候才給該頁(yè)表分配 RAM。

頁(yè)目錄項(xiàng)和頁(yè)表項(xiàng)的結(jié)構(gòu)如下:

  • Present 標(biāo)志:為 1 則表示頁(yè)在主存中;如果為 0 則表示不在內(nèi)存中,如果執(zhí)行一個(gè)地址轉(zhuǎn)換的時(shí)候,所需的頁(yè)表項(xiàng)或者頁(yè)目錄項(xiàng)中的該標(biāo)志為 0,那么分頁(yè)單元就把該線性地址存在在控制寄存器 cr2 中,并產(chǎn)生 14 號(hào)異常:缺頁(yè)異常。
  • 包含頁(yè)框物理地址最高 20 位的字段
  • Dirty:當(dāng)對(duì)頁(yè)框進(jìn)行寫(xiě)操作時(shí)就設(shè)置這個(gè)標(biāo)志
  • Read/Write 標(biāo)志:含有頁(yè)或者頁(yè)表的存取權(quán)限
  • User/Supervisor:含有訪問(wèn)頁(yè)或者頁(yè)表所需的特權(quán)等級(jí)

了解了以上結(jié)構(gòu)之后,我們看看如何從線性地址轉(zhuǎn)換到物理地址的:

  • 線性地址中的 Directory 字段決定頁(yè)目錄中的目錄項(xiàng),目錄項(xiàng)指向適當(dāng)?shù)捻?yè)表
  • 線性地址中的 Table 字段又決定頁(yè)表的頁(yè)表項(xiàng),頁(yè)表項(xiàng)含有頁(yè)所在頁(yè)框的物理地址
  • 線性地址中的 Offset 地段決定了頁(yè)框內(nèi)的相對(duì)位置,由于 offset 為 12 為,所以一頁(yè)含有 4096 字節(jié)的數(shù)據(jù)

以上描述的為 80x86 微處理器硬件分頁(yè)機(jī)制,不同架構(gòu)的 64 位處理器分頁(yè)機(jī)制,大體的思路就是將二級(jí)模式拓展為三級(jí)(ia64)或者四級(jí)(x86_64),以達(dá)到對(duì)更大范圍尋址空間的支持。具體到 Linux 中如何使用操作系統(tǒng)的分段分頁(yè)機(jī)制以及進(jìn)程的地址空間管理,后續(xù)再談

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容