windows內存分段

1.3.2  Windows的內存管理html

在這一節中,讀者能夠解決初學Win32彙編的兩個大疑問:編程

● Win32彙編中,每一個程序均可以用4 GB的內存嗎?安全

● Win32彙編源代碼中爲何看不到CS,DS,ES和SS等段寄存器的使用?操作系統

1. DOS操做系統的內存安排設計

Win32編程相對於DOS編程最大的區別之一就是內存的使用。視頻

先來回顧一下DOS操做系統的內存使用,如圖1.1所示。DOS操做系統運行於實模式中,因爲8086處理器的尋址範圍只有1 MB,當時把系統硬件使用的內存安排在高端,地址是從A0000h(即640 KB)開始的384 KB中,其中有用於顯示的視頻緩衝區和BIOS的地址空間。而在內存低端,安排了中斷向量表和BIOS數據區;剩下從500h開始到A0000h總共不到640 KB的內存是操做系統和應用程序所可以使用的;應用程序不可能使用這640 KB之外的內存。這就是著名的「640KB限制」。但即便在這640 KB中,DOS操做系統又佔領了低端的一部份內存,最後剩下600 KB左右的內存纔是應用程序真正能夠用的。若是系統中有內存駐留程序存在,那麼應用程序還要和這些TSR程序共同分享這段內存空間。htm

當80386處理器推出後,能夠尋址的內存範圍達到了4 GB,利用XMS驅動程序能夠訪問到全部的4 GB地址空間。但16位的段尋址方式限制了DOS程序,「可見」的內存範圍仍是停留在00000h到FFFF0h+64 KB的範圍內,全部高於1 MB的擴展內存只能經過XMS驅動程序當作數據交換使用,程序的執行空間並無什麼增長。教程

2. 80386的內存尋址機制索引

Windows的內存管理和DOS的內存管理有很大的不一樣,在瞭解Windows的內存管理模式以前,須要對80386保護模式下內存分頁機制有些瞭解。爲了作個對比,先來看實模式下的內存尋址方式,即DOS下的尋址方式,如圖1.2所示。ip


圖1.2  實模式下的內存尋址方式

在實模式下,一個完整的地址由段地址和偏移地址兩部分組成。段地址放在16位的段寄存器中,而後在指令中用16位的偏移地址尋址。處理器換算時先將段地址乘以10h,獲得段在物理內存中的起始地址;而後加上16位的偏移地址獲得實際的物理地址。如xxxx:yyyy格式的虛擬地址在內存中的實際位置是xxxx×10h+yyyy。

當80386處理器工做在保護模式和虛擬8086模式的時候,可使用所有32根地址線訪問4 GB大的內存。段地址加偏移地址的計算方法顯然沒法覆蓋這麼大的範圍。但計算一下就能夠發現,實際上和8086一樣的限制已經不復存在,由於80386全部的通用寄存器都是32位的,2的32次方至關於4G,因此用任何一個通用寄存器來間接尋址,沒必要分段就已經能夠訪問到全部的內存地址。

這是否是說,在保護模式下,段寄存器就再也不有用了呢?答案是否認的。實際上段寄存器更有用了,雖然在尋址上再也不有分段的限制問題,但在保護模式下,一個地址空間是否能夠被寫入,能夠被多少優先級的代碼寫入,是否是容許執行等涉及保護的問題就出來了。要解決這些問題,必須對一個地址空間定義一些安全上的屬性。段寄存器這時就派上了用途。可是涉及屬性和保護模式下段的其餘參數,要表示的信息太多了,要用64位長的數據才能表示。咱們把這64位的屬性數據叫作段描述符(Segment Descriptor)。 (段描述 符中除了包含基址外,還包含其餘安全上的屬性)

80386的段寄存器是16位的,沒法放下保護模式下64位的段描述符。如何解決這個新的問題呢?解決辦法是把全部段的段描述符順序放在內存中的指定位置,組成一個段描述符表(Descriptor Table);而段寄存器中的16位用來作索引信息,指定這個段的屬性用段描述符表中的第幾個描述符來表示。這時,段寄存器中的信息再也不是段地址了,而是段選擇器(Segment Selector)。能夠經過它在段描述符表中「選擇」一個項目以獲得段的所有信息。

既然這樣,段描述符表放在那裏呢?80386中引入了兩個新的寄存器來管理段描述符表。一個是48位的全局描述符表寄存器GDTR,一個是16位的局部描述符表寄存器LDTR。那麼,爲何有兩個描述符表寄存器呢?

GDTR指向的描述符表爲全局描述符表GDT(Global Descriptor Table)。它包含系統中全部任務均可用的段描述符,一般包含描述操做系統所使用的代碼段、數據段和堆棧段的描述符及各任務的LDT段等;全局描述符表只有一個。(note:因此只有一個限長,放在了GDTR中,這裏的GDTR意思是全局段描述符表寄存器。)

LDTR則指向局部描述符表LDT(Local Descriptor Table)。80386處理器設計成每一個任務都有一個獨立的LDT。它包含有每一個任務私有的代碼段、數據段和堆棧段的描述符,也包含該任務所使用的一些門描述符,如任務門和調用門描述符等。(raynote:雖然每一個任務都有一個獨立的LDT,可是LDTR即段描述符表寄存器只有一個。)

不一樣任務的局部描述符表分別組成不一樣的內存段(???),描述這些內存段(note:指局部描述符表所在的內存段,而不是任務私有段)的描述符當作系統描述符放在全局描述符表中。和GDTR直接指向內存地址不一樣,LDTR和CS,DS等段選擇器同樣只存放索引值,指向局部描述符表內存段對應的描述符在全局描述符表中的位置(???全局描述符表中存放了局部描述符表所在內存段的描述符信息。畢竟描述符表自己也是要在內存中存儲的,要有對應的描述符信息)。隨着任務的切換,只要改變LDTR的值,系統當前的局部描述符表LDT也隨之切換,這樣便於各任務之間數據的隔離。但GDT並不隨着任務的切換而切換。

看到這裏,讀者可能會提出一個問題,既然有全局描述符表和局部描述符表兩個表,那麼段選擇器中的索引值對應哪一個表中的描述符呢。實際上,16位的段選擇器中只有高13位表示索引值。剩下的3個數據位中,第0,1位表示程序的當前優先級RPL;第2位TI位用來表示在段描述符的位置;TI=0表示在GDT中,TI=1表示在LDT中。

以圖1.3爲例,在保護模式下,一樣以xxxx:yyyyyyyy格式表示一個虛擬地址。單單憑段選擇器中的數值xxxx根本沒法反映出段的基址在哪裏。對於這個地址,首先要看xxxx的TI位是否爲0,若是是的話,則先從GDTR寄存器中獲取GDT的基址(圖中的步驟①),而後在GDT中以段選擇器xxxx的高12位當作位置索引獲得段描述符(步驟②)。段描述符包含段的基址、(note:雖然32位寄存器徹底能夠直接表示一個地址,但保護模式下仍是會分段)限長、優先級等各類屬性,這就獲得了段的起始地址(步驟③);若是xxxx的TI位爲1的話就更復雜了,這表示段描述符在LDT中,這時第一步的操做仍是從GDTR寄存器中獲取GDT的基址(步驟1'),而且要從LDTR中獲取LDT(note:指一個描述符表)所在段的位置索引(步驟2',note:全部的位置索引都是指在GDT中的);而後以這個位置索引在GDT中獲得LDT段的位置(步驟3');而後纔是用(note:段選擇器中的)xxxx作索引從LDT段中得到段描述符(步驟4'),再以這個段描述符獲得段的基址等信息(步驟5')。分這兩種狀況獲得段的基址後(圖中Result所示),再以基址加上偏移地址yyyyyyyy才獲得最後的線性地址。

關於段描述符的格式定義,讀者能夠參考其餘講述保護模式的書籍。


圖1.3 保護模式下GDTR,LDTR,全局描述符表,局部描述符表和選擇器的關係

總結:

GDTR中的基址是直接指向內存地址,佔32位。

每一個任務 有本身 獨立的LDT,從LDTR中得到的是當前任務的局部描述符表所在內存段在全局描述符表中位置 索引。

每一個任務 的局部描述符表LDT有不少 條目 ,段選擇器,在TI=1的狀況下,前面 的12位是在ldt中的位置 索引 。

LDTR中第三位是作什麼 用??

WIN32彙編語言教程:第01章 背景知識 · 1.3 必須瞭解的東西(2)
http://www.feiesoft.com/win32asm/win32asm-1-5.html

相關文章
相關標籤/搜索