內存訪問全過程

這一篇,是重點!咱們將去講解操做系統根據代碼(邏輯)地址去訪問真實物理地址的全過程。操作系統

將把全面幾節的東西所有用上,並徹底梳理,完善細節。翻譯

前面講了分段、分頁機制,他們均可以實現,從虛擬地址(地址空間)向物理地址的轉換。可是,實際使用過程當中,使用的是分段+分頁機制,段頁結合。3d

段頁結合

全過程分析(高能)

咱們如今採用邊實驗邊講解翻譯全過程。調試

寫了一段 c 代碼,編譯,而後在 Linux 0.11 中,進行調試code

#include <stdio.h>

int i = 0x12345678;
int main(void)
{
    printf("The logical/virtual address of i is 0x%08x", &i);
    fflush(stdout);
    while (i)
        ;
    return 0;
}

注意:咱們程序中的變量 i 的大小爲 0x12345678。blog

咱們想作的是,經過編譯,找到變量 i 的邏輯地址,而後通過一系列的地址轉換,得到物理地址。經過查看物理地址的內容,是不是 0x12345678。索引

得到虛擬地址

將運行的代碼進行反編譯,能夠看到 cmp dword ptr 這一部分。這一部分,對應的就是上面c語言的 while(i) 部分。進程

能夠看到熟悉的 ds:0x3004,這是什麼?內存

這就是咱們以前分段章節裏面的間接尋址。也就是說,咱們要找到 ds 段的基址,而後加上3004的偏移量。io

這裏的 ds:0x3004 就是這一部分。你會發現 0x3004 只有16位啊,下圖的偏移量標記的是32位。

由於在 Linux 0.11中,給每一個進程劃分了 64M 的虛擬內存,2的16次方就是64M。

下圖中的偏移量位32位,是給每一個進程劃分了 4G 的虛擬內存。

注意:看下圖紅色方框部分,其中的0-15位選擇符用來選擇程序中的段的。後面的0-31偏移值,是每一個段中的偏移量。

分段機制,假設一個程序中有不少個段(個數由選擇符的位數決定),並且每一個段均可以佔有一個大小的空間(由偏移值位數決定)。

在下圖中,因爲選擇符0-15中只有14位用來指定段的,因此下圖中的虛擬地址,能夠指定214個段,每一個段能夠有4G(232)的大小空間。

虛擬地址解讀

從上面,咱們得到變量 i 的虛擬地址爲 ds: 0x3004。

經過下圖,咱們查看寄存器,能夠得到ds=0x0017,因此ds:0x3004=0x0017: 3004。

咱們來看ds=0x0017的解讀。

這其實也叫選擇符,看下圖。

重點看,TI 位,也就是2號位。0x0017=0x 17 = 0x 0001 0111,也就是 TI 位爲1。

當 TI 爲0時,說明咱們要找的有關段表信息就在 GDT表中,咱們能夠經過繼續對 0x0017的3-15位進行解讀,獲取有關段表信息在 GDT表中的索引。

當 TI 爲1的時候,說明咱們要找的 有關段表信息 在 LDT表中。

段描述符(段表的相關信息)

段描述符

每一個段都有一個段描述符。

段描述符指定段的大小、訪問權限和段的特權級、段類型以及段的第一字節在線性地址空間中的位置(也就是段基址)。

GDT表

GDT表,是全局描述表。從這裏的 描述 二字與上面的 段描述符能夠看出:GDT表中保存着上面提到的段描述符。

LDT表

LDT表,是局部描述表。裏面也保存着段描述符。

GDTR寄存器

此寄存器,記錄着 GDT表的基址。

LDTR寄存器

跟咱們以前說的選擇符是同樣的,它代表了 LDT表在 GDT表中的位置。

咱們能夠這樣理解GDT和LDT:GDT爲一級描述符表,LDT爲二級描述符表。

LDT和GDT從本質上說是相同的,只是LDT嵌套在GDT之中。LDTR記錄局部描述符表的起始位置,與GDTR不一樣,LDTR的內容是一個段選擇子。因爲LDT自己一樣是一段內存,也是一個段,因此它也有個描述符描述它,這個描述符就存儲在GDT中,對應這個表述符也會有一個選擇子,LDTR裝載的就是這樣一個選擇子。

注意,LDT表中也保存着描述符,是咱們須要的。

也就是說,咱們首先要獲取 LDT表的描述符,而後在 LDT表中獲取咱們須要的段描述符。

得到LDT段描述符

咱們已經知道,咱們的段選擇符爲ldtr。

因此,咱們如今得得到 GDTR 和 LDTR寄存器中的內容。

能夠看到,LDTR寄存器中的值爲 0x0068, GDTR寄存器中的值爲 0x00005cb8。

因此,咱們將0x0068=0000 0000 0110 1000,咱們保留3-15位,1101=13。

因此咱們如今知道了,咱們須要的段描述符在GDT表開始位置的第13個位置處。

咱們在GDT表得到偏移13個位置處的內容。

解讀段描述符

咱們已經得到了段描述符的內容了,離目標愈來愈近了。只要解讀出段描述符的內容,咱們就能夠得到段表的基址了。

其解讀以下,咱們利用上面的結果,並結合下圖,去得到基址。

因此,咱們得到 LDT表的物理地址爲0x00fd52d0。

獲取咱們所需段表的描述符

就像咱們以前談到的,LDT表存儲的也是段描述符。

因此咱們也須要像以前那樣,去獲取相應位置的段描述符,而後進行解讀。

還記得咱們以前的ds=0x0017嘛?

0x0017=0x 17 = 0x 0001 0111,其中索引爲2。

如今咱們得到 LDT表基址開始處的內容。

由於索引都是從0開始的,因此獲取的段描述符爲 0x00003ffff 0x10c0f300。

解讀方式如上,這樣咱們求得段表的基址爲0x10000000。

以後,將段基址與偏移量相加,便可得到線性地址。0x10003004。

線性地址解讀

由於採用了多級頁表,因此分頁頁目錄和頁表。其中位數解讀,如圖所示。

注意,頁目錄的基址存儲在 cr3 寄存器中。

以下圖,咱們得到的頁目錄表的基址爲0x0。

說明頁目錄表的基址爲 0。

由於0x10003004=0x 0001 0000 0000 0000 0011 0000 0000 0100。

因此知道,目錄爲 0001 0000 00 ,爲64。

頁面爲 00 0000 0011,爲3。

偏移爲0000 0000 0100,爲4。

獲取頁目錄項

咱們要得到頁目錄號爲64的內容:

解讀頁表項

能夠看到基址爲12到31爲,因此地址爲0x00fa5000。

獲取頁表項

頁表所在物理頁框爲0x00fa5000位置,從該位置開始查找3號頁表項,獲得:

這個解讀同上,因此最後得到的基址爲0x00f99000。

加上前面提到的偏移4,最終的物理地址爲:0x00f99004。

驗證

最後,咱們查看這個物理地址的內容,發現,是咱們程序中設置的i的值。

相關文章
相關標籤/搜索