第 1 章 背景知識
1.1 Win32的軟硬件平臺
1.2 Windows的特色
1.3 必須了解的東西
1.3.1 80x86處理器的工作模式
??? 80386處理器有3種工作模式:實模式、保護模式和虛擬86模式。
??? 保護模式是80386處理器的主要工作模式。在此方式下,80386可以尋址4 GB的地址空間,同時,保護模式提供了8038的多任務、內存分頁管理和優先級保護等機制。
??? 虛擬86模式是為了在保護模式下提供和8086處理器的兼容,在虛擬86模式下,同樣支持任務切換、內存分頁管理和優先級,但內存的尋址方式和8086相同,也是可以尋址1MB的空間。
??? 1. 實模式
??? 80386處理器被復位或加電的時候以實模式啟動。80386處理器在實模式下的存儲器尋址方式和8086是一樣的,由段寄存器的內容乘以16當做基地址,加上段內的偏移地址形成最終的物理地址,這時候它的32位地址線只使用了低20位。在實模式下,80386處理器不能對內存進行分頁管理,所以指令尋址的地址就是內存中實際的物理地址。在實模式下,所有的段都是可以讀、寫和執行的。實模式下80386不支持優先級,所有的指令相當于工作在特權級(優先級0),所以它可以執行所有特權指令。
??? 2. 保護模式
??? 當80386工作在保護模式下的時候,它的所有功能都是可用的。這時80386所有的32根地址線都可供尋址,物理尋址空間高達4 GB。在保護模式下,支持內存分頁機制,提供了對虛擬內存的良好支持。在保護模式下,80386處理器還支持優先級機制。優先級分4個級別(0級~3級),操作系統運行在最高的優先級0上,應用程序則運行在比較低的級別上。
??? DOS操作系統運行于實模式下,而Windows操作系統運行于保護模式下。
??? 3. 虛擬86模式
??? 虛擬86模式是為了在保護模式下執行8086程序而設置的。雖然80386處理器已經提供了實模式來兼容8086程序,但這時8086程序實際上只是運行得快了一點,對CPU的資源還是獨占的。虛擬86模式是以任務形式在保護模式上執行的,在80386上可以同時支持由多個真正的80386任務和虛擬86模式構成的任務。在虛擬86模式下,80386支持任務切換和內存分頁。在Windows操作系統中,有一部分程序專門用來管理虛擬86模式的任務,稱為虛擬86管理程序。為了和8086程序的尋址方式兼容,虛擬86模式采用和8086一樣的尋址方式,即用段寄存器乘以16當做基址再配合偏移地址形成線性地址,尋址空間為1 MB。但顯然多個虛擬86任務不能同時使用同一位置的1 MB地址空間,否則會引起沖突。操作系統利用分頁機制將不同虛擬86任務的地址空間映射到不同的物理地址上去,這樣每個虛擬86任務看起來都認為自己在使用0~1 MB的地址空間。
1.3.2 Windows的內存管理
??? 1. DOS操作系統的內存安排
??? DOS操作系統運行于實模式中,由于8086處理器的尋址范圍只有1 MB,當時系統硬件使用的存儲器地址被安排在高端,地址是從A0000h(即640 KB)開始的384 KB中,其中有用于顯示的視頻緩沖區和BIOS的地址空間。而在內存低端,安排了中斷向量表和BIOS數據區;剩下從500h開始到A0000h總共不到640 KB的內存是操作系統和應用程序所能夠使用的;應用程序不可能使用這640 KB以外的內存。這就是著名的“640KB限制”。
??? 2. 80386的內存尋址機制
??? 因為80386所有的通用寄存器都是32位的,232相當于4G,所以用任何一個通用寄存器來間接尋址,不必分段就已經可以訪問到所有的內存地址。這是不是說,在保護模式下,段寄存器就不再有用了呢?答案是否定的。雖然在尋址上不再有分段的限制問題,但在保護模式下,一個地址空間是否可以被寫入,可以被多少優先級的代碼寫入,是不是允許執行等涉及保護的問題就出來了。要解決這些問題,必須對一個地址空間定義一些安全上的屬性。段寄存器這時就派上了用途。但是涉及屬性和保護模式下段的其他參數,要表示的信息太多了,要用64位長的數據才能表示。我們把這64位的屬性數據叫做段描述符(Segment Descriptor)。把所有段的段描述符順序放在內存中的指定位置,組成一個段描述符表(Descriptor Table);而段寄存器中的16位用來做索引信息,指定這個段的屬性用段描述符表中的第幾個描述符來表示。這時,段寄存器中的信息不再是段地址了,而是段選擇器(Segment Selector)??梢酝ㄟ^它在段描述符表中“選擇”一個項目以得到段的全部信息。
??? 80386中引入了兩個新的寄存器來管理段描述符表。一個是48位的全局描述符表寄存器GDTR,一個是16位的局部描述符表寄存器LDTR。GDTR指向的描述符表為全局描述符表GDT(Global Descriptor Table)。它包含系統中所有任務都可用的段描述符,通常包含描述操作系統所使用的代碼段、數據段和堆棧段的描述符及各任務的LDT段等;全局描述符表只有一個。
??? LDTR則指向局部描述符表LDT(Local Descriptor Table)。80386處理器設計成每個任務都有一個獨立的LDT。它包含有每個任務私有的代碼段、數據段和堆棧段的描述符,也包含該任務所使用的一些門描述符,如任務門和調用門描述符等。
??? 3. 80386的內存分頁機制
??? 80386處理器把4 KB大小的一塊內存當做一“頁”內存,每頁物理內存可以根據“頁目錄”和“頁表”,隨意映射到不同的線性地址上。這樣,就可以將物理地址不連續的內存的映射連到一起,在線性地址上視為連續。頁表可以指定一個頁面并不真正映射到物理內存中。這樣,訪問這個頁的指令會引發頁異常錯誤。這時,處理器會自動轉移到頁異常處理程序中去。操作系統可以在異常處理程序中將硬盤上的虛擬內存讀到內存中并修改頁表重新映射,然后重新執行引發異常的指令。這樣指令可以正常執行下去。
??? 4. Windows的內存安排
??? 如果把虛擬內存暫時先視為物理內存的一部分,從物理內存的層次看,Windows操作系統和DOS一樣,也是所有的內容共享內存。但是從應用程序代碼的層次看,也就是說從分頁映射后線性地址的層次看,內存的安排卻不是這個樣子。因為Windows是一個分時的多任務操作系統,CPU時間被分成一個個的時間片后分配給不同程序輪流使用,在一個程序的時間片中,和這個程序執行無關的東西(如其他程序的代碼和數據)并不需要映射到線性地址中去。在物理內存中,操作系統和系統DLL的代碼需要供每個應用程序調用,所以在所有的時間片中都必須被映射;用戶程序只在自己所屬的時間片內被映射;而用戶DLL則有選擇地被映射。假設程序A和程序C都要用到xxx.dll,那么物理內存中xxx.dll的代碼在圖中的時間片1和n中被映射,其他的時間片就不需要映射,當然,物理內存中只需要一份xxx.dll的代碼。
??? ● 每個應用程序都有自己的4 GB的尋址空間。該空間可存放操作系統、系統DLL和用戶DLL的代碼,它們之中有各種函數供應用程序調用。再除去其他的一些空間,余下的是應用程序的代碼、數據和可以分配的地址空間。
??? ● 不同應用程序的線性地址空間是隔離的。雖然它們在物理內存中同時存在,但在某個程序所屬的時間片中,其他應用程序的代碼和數據沒有被映射到可尋址的線性地址中,所以是不可訪問的。從編程的角度看,程序可以使用4 GB的尋址空間,而且這個空間是“私有”的。
??? ● DLL程序沒有自己“私有”的空間。它們總是被映射到其他應用程序的地址空間中,當做其他應用程序的一部分運行。原因很簡單,如果它不和其他程序同屬一個地址空間,應用程序該如何調用它呢?
??? 5. 從Win32匯編的角度看內存尋址
??? Win32匯編中的內存訪問為用戶程序的代碼段、數據段和堆棧段全部預定義好了段描述符。這些段的起始地址為0,限長為ffffffff,所以用它們可以直接尋址全部的4 GB地址空間。程序開始執行的時候,CS,DS,ES和SS都已經指向了正確的描述符,在整個程序的生命周期內,程序員不必改動這些段寄存器,也不必關心它們的值究竟是多少(實際上是想改也改不了)。
??? 1.3.3 Windows的特權保護
??? 1. 80386的中斷和異常
??? 8086或80386實模式下中斷和異常的處理過程:實模式下的中斷和異常服務程序地址存放在中斷向量表中。中斷向量表位于物理內存00000h開始的400h字節中,共支持100h個中斷向量;每個中斷向量是一個xxxx:yyyy格式的地址,占用4字節。當發生n號異?;騨號中斷,或者執行到int n指令的時候,CPU首先到內存n×4的地方取出服務程序的地址aaaa:bbbb;然后將標志寄存器、中斷時的CS和IP壓入堆棧,接著轉移到aaaa:bbbb處執行;在服務程序最后遇到iret的時候,CPU從堆棧中恢復標志寄存器,然后取出CS和IP并返回。
??? 在保護模式下,中斷或異常處理往往從用戶代碼切換到操作系統代碼中執行。由于保護模式下的代碼有優先級之分,為了使高優先級的代碼能夠安全地被低優先級的代碼調用,保護模式下增加了“門”的概念?!伴T”指向某個優先級高的程序所規定的入口點,所有優先級低的程序調用優先級高的程序只能通過門重定向,進入門所規定的入口點。保護模式下的中斷和異常等服務程序也要從“門”進入,80386的門分為中斷門、自陷門和任務門幾種。
??? 在保護模式下要表示一個中斷或異常服務程序的信息需要用8個字節,包括門的種類以及xxxx:yyyyyyyy格式的入口地址等。這組信息叫做“中斷描述符”。保護模式下把所有的中斷描述符放在一起組成“中斷描述符表”IDT(Interrupt Descriptor Table)。IDT不再放在固定的地址00000h處,而是采用可編程設置的方式,支持的中斷數量也可以設置。為此80386處理器引入了一個新的48位寄存器IDTR。IDTR的高32位指定了IDT在內存中的基址(線性地址),低16位指定了IDT的長度,相當于指定了可以支持的中斷數量。
??? 2. 80386的保護機制
??? 保護機制主要由下列幾方面組成:
??? ● 段的類型檢查——段的類型是由段描述符指定的,主要屬性有是否可執行,是否可讀和是否可寫等。而CS,DS和SS等段選擇器是否能裝入某種類型的段描述符是有限制的。如不可執行的段不能裝入CS;不可讀的段不能裝入DS與ES等數據段寄存器;不可寫的段不能裝入SS等。如果段類型檢查通不過,則處理器會產生一般性保護異常或堆棧異常。
??? ● 頁的類型檢查——除了可以在段級別上指定整個段是否可讀寫外,在頁表中也可以為每個頁指定是否可寫。對于特權級下的執行代碼,所有的頁都是可寫的。但對于1,2和3級的代碼,還要根據頁表中的R/W項決定是否可寫,企圖對只讀的頁進行寫操作會產生頁異常。
??? ● 訪問數據時的級別檢查——優先級低的代碼不能訪問優先級高的數據段。80386的段描述符中有一個DPL域(描述符優先級),表示這個段可以被訪問的最低優先級。而段選擇器中含有RPL域(請求優先級),表示當前執行代碼的優先級。只有DPL在數值上大于或等于RPL值的時候,該段才是可以訪問的,否則會產生一般性保護異常。
??? ● 控制轉移的檢查——在處理器中,有很多指令可以實現控制轉移,如jmp,call,ret,int和iret等指令。但優先級低的代碼不能隨意轉移到優先級高的代碼中,所以遇到這些指令的時候,處理器要檢查轉移的目的位置是否合法。
??? ● 指令集的檢查——有兩類指令可以影響保護機制。第一類是改變GDT,LDT,IDT以及控制寄存器等關鍵寄存器的指令,稱為特權指令;第二類是操作I/O端口的指令以及cli和sti等改變中斷允許的指令,稱為敏感指令。試想一下,如果用戶級程序可以用sti禁止一切中斷(包括時鐘中斷),那么整個系統就無法正常運行,所以這些指令的運行要受到限制。特權指令只能在優先級0上才能運行,而敏感指令取決于eflags寄存器中的IOPL位。只有IOPL位表示的優先級高于等于當前代碼段的優先級時,指令才能執行。
??? ● I/O操作的保護——I/O地址也是受保護的對象。因為通過I/O操作可以繞過系統對很多硬件進行控制。80386可以單獨為I/O空間提供保護,每個任務有個TSS(任務狀態段)來記錄任務切換的信息。TSS中有個I/O允許位圖,用來表示對應的I/O端口是否可以操作。某個I/O地址在位圖中的對應數據位為0則表示可以操作;如果為1則還要看eflags中的IOPL位,這時只有IOPL位表示的優先級高于等于當前代碼段的優先級,才允許訪問該I/O端口。
??? 3. Windows的保護機制