天天看點

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

邏輯位址(logical address):包含在機器語言中用來指定一個操作數或一條指令的位址。每個邏輯位址都由一個端(segment)和偏移量(offset或displacement)組成,偏移量指明了從段開始的地方到實際位址之間的距離。

線性位址(linear address)(也稱虛拟位址 virtual address):是一個32位無符号整數,可以用來表示高達4gb的位址。線性位址通常用十六進制數字來表示,值的範圍從0x00000000到0xffffffff。

實體位址(physical address):用于記憶體晶片級記憶體單元尋址。它們與從微處理器的位址引腳發送到記憶體總線上的電信号相對應。實體位址由32為或36位無符号整數表示。

記憶體條硬體圖:

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

記憶體晶片尋址的基本原理:

在存儲器中儲存着大量資料。為了友善儲存和提取這些資料,必須為這些資料編上号。但是,這個序号不是按系列順序編址的,而是按行和列的順序編址的。是以,當提取它們的時侯,隻要指明行數(稱行址)和列數(稱列址)就可以很輕易地尋找到它們。這就是記憶體晶片尋址的基本原理。記憶體晶片的内部結構就相當是一張大的矩陣網,由行(row)線和列(column)線互相交叉得到一系列單元格(cell),資料都被依序放在單元格中。我們把矩陣中的每個單元格稱為單元(cell),它是記憶體中存儲資料的最小邏輯機關。把這個大矩陣就稱為邏輯存儲塊(logical

bank),簡寫為l-bank。

 記憶體條抽象圖:

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

記憶體控制單元(存儲器管理單元 mmu)通過分段單元(segmentation unit)的硬體電路把一個邏輯位址轉換為線性位址,接着,分頁單元(paging unit)硬體電路把線性位址轉換為實體位址。

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

邏輯位址:段選擇符(segment selector,16位)+段内偏移(offset,32位)

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

index:在gdt或ldt中段描述符的位置。

ti:段描述符在gdt中(ti=0),段描述符在ldt中(ti=1)。

rpl:請求者特權級,當段選擇符裝入cs寄存器中,訓示cpu的目前特權級。

為了友善的找到段選擇符,處理器提供六個段寄存器存放段選擇符。分别是:

cs ss ds es fs gs

其中,

cs:代碼段寄存器,指向包含程式指令的段。

ss:棧段寄存器,指向包含目前程式棧的段。

ds:資料段寄存器,指向包含靜态數或者全局資料段。

其他三個段寄存器作一般用途,可以指向任意的資料段。

cs寄存器的rpl字段(兩位字段)表示cpu的目前特權級(cpl),核心态0,使用者态3。

每個段由一個8位元組的段描述符表示,它描述了段的特征。

全局描述符表(gdt)和局部描述符表(ldt)用于存放段描述符。

通常隻定義一個gdt,而每個程序除了存放在gdt中的段之外,如果還需要建立附加的段,就可以有自己的ldt。

gdt在主存中的位置和大小存放在gdtr控制寄存器中,目前正在被使用的ldt位址和大小放在ldtr寄存器中。

段描述符關鍵字段:

base: 段基位址

g :粒度标志:清0,段大小以位元組為機關,否則以4096位元組為機關。

limit:存放段中最後一個記憶體單元的偏移量,即段長度。

s:系統标志,0:系統段,存儲諸如ldt之類關鍵資料結構;否則,普通代碼段或資料段。

type:描述了段的類型和它的存取權限。

dpl:描述符特權級(descriptor privilege level)字段,用于存取這個段都要求的特權級,表示為通路這個段而要求的cpu最小的優先級。

段的類型及對應段描述符:

代碼段描述符:

表示這個段描述符代表一個代碼段,它可以放在gdt或ldt中。該描述符置s辨別為1(非系統段)。

資料段描述符:

表示這個段描述符代表一個資料段,它可以放在gdt或ldt中。該描述符置s辨別為1,棧段是通過一般的資料段實作的。

任務狀态段描述符(tssd):

表示這個段描述符代表一個任務狀态段(task state segment,tss)。也就是說這個段用于儲存處理器寄存器的内容。它隻能出現在gd中。該描述符置s标志為0(系統段)。

局部描述符表描述符(ldtd)

表示這個段描述符代表一個包含ldt的段,它隻出現在gdt中,相應的type字段的值為2,s标志置為0(系統段)。

結構:

Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段
Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段
Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段
Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段
Linux核心剖析 之 記憶體尋址(一) 1、記憶體位址 2、邏輯位址->線性位址(分段:硬體+Linux) 3、Linux中的分段

具體操作:

*檢查段選擇符的ti字段,确定段描述符儲存在哪一個描述表中。ti字段指明描述符是在gdt中還是在激活的ldt中,分段單元根據ti字段從gdtr或ldtr寄存器中獲得gdt或ldt的線性基位址。

*從段選擇符的index字段計算出段描述符的位址,index字段的值乘以8(一個段描述符的大小),此結果與gdtr或ldtr寄存器中的内容相加。

*把邏輯位址的偏移量與段描述符base字段的值相加,即得到線性位址。

linux以非常有限的方式使用分段。

四個主要的linux段的段描述符:

base

g

limit

s

type

dpl

d/b

p

使用者代碼段

0x00000000

1

0xfffff

10

3

使用者資料段

2

核心代碼段

核心資料段

相應的段選擇符由宏__user_cs,__user_ds,__kernel_cs,__kernel_ds分别定義。

例如,為了對核心代碼段尋址,隻需要将__kernel_cs宏産生的值裝入cs寄存器中即可。

所有段的段基位址都為0x00000000,位址空間都是從0~232-1。

可就是,linux下邏輯位址與線性位址是一緻的,邏輯位址的偏移量字段與相應線性位址的值總是一一對應的。

一個cpu對應一個gdt,所有的gdt都存放在cpu_gdt_table數組中。

所有gdt的位址和它們的大小被存放在cpu_gdt_descr數組中。

linuxgdt:

linux全局描述符表

段選擇符

null

0x0

tss

0x80

reserved

ldt

0x88

pnpbios 32-bit code

0x90

pnpbios 16-bit code

0x98

pnpbios 16-bit data

0xa0

0xa8

tls #1

0x33

0xb0

tls #2

0x3b

apmbios 32-bit code

0xb8

tls #3

0x43

apmbios 16-bit code

0xc0

apmbios data

0xc8

not used

kernel code

kernel data

user code

user data

double fault tss

0xf8

每個gdt包含18個段描述符和14個空的,未使用的或保留的項。插入未使用的項的目的是為了使經常一起通路的描述符能夠處于同一個32位元組的硬體高速緩沖行中。

每一個gdt中包含的18個段描述符指向下列的段:

*3個局部線程存儲段(thread-local storage,tls):線程私有資料,系統調用set_thread_area()和get_thread_area()分别為正在執行的程序建立和撤銷一個tls段。

*4個使用者态和核心态下的代碼段,資料段。

*tss段:任務狀态段,每個cpu一個。所有的任務狀态段都順序存放在init_tss數組中。

#第n個cpu的tss描述符的base字段指向init_tss數組的第n個元素。

#g标志清0,limit為0xeb,也就是段長為236位元組。type字段置為9或11。dpl為0,不允許使用者态通路。

#程序切換,程序上下文切換時,這個段用于儲存cpu寄存器的内容。

*ldt段:一般指向包含預設ldt表的段。(大多數使用者态下程式都不使用ldt,是以定義一個預設的ldt供大多數程序共享)。

        #預設的局部描述符表存放在default_ldt數組中,它包含5個項,但核心僅僅有效地使用了其中的兩個項(用于ibcs執行檔案的調用門和solaris/x86可執行檔案的調用門)。modify_ldt()系統調用允許程序建立自己的局部描述符表(例如wine程式),此時ldt段相應的被修改。

*double fault tss:處理雙重錯誤異常的特殊的tss段。

*3個與進階電源管理(amp)有關的段。

*5個與支援pnp功能的bios服務有關的段。

繼續閱讀